Blending colors on a texture: shader, render texture, or other

I want to write on a texture and be able to blend colors together. For example, if the user colors a yellow area, and the picks a transparant blue, coloring over the yellow will produce green.
I’ve tried a couple different ways with varying success. Here a couple of them:

  • new pixel = base pixel * base pixel alpha + brush pixel * brush pixel alpha
    this one worked pretty well, but the overlapping brush strokes became very visible. In order to remove the brush circle stroke, I stored the coordinates of the last set of pixels that were changed in an array, and then checked to see if any of the pixels I was setting had been changed on the previous pass so that I wouldn’t rewrite over anything I had already changed. This comparison made drawing slow on the device.

  • writing on a transparant buffer, and then averaging the colors together
    this one worked, but was the slowest of all. It was pretty similar to the last method, but instead of storing the last set of pixels that were changed, I just wrote on a buffer and applied on mouse up.

  • I tried playing with render textures, but I keep running into scaling issues, and I don’t have much experience with them, so I’m probably doing something wrong.

  • I looked at a couple shaders, but I don’t know Cg and have never made a shader. It looks like what I want done could be quite easily with a shader though.

I’m quite stumped. I wish I was more familiar with shaders and render textures, as they seem to be quite powerful if you know how to use them.

Seen here is the overlapping transparent brush strokes and the problem they create. I need an efficient way to get rid of them.

solution 1

very similar to your solution 1, but use blend formula:
final color = base color * (1 - brush alpha) + brush pixel * brush alpha

and you needn’t to store any previous actions

solution 2

just place a transparent planes (each is 4 vertices + 2 triangles) with texture of brush and color customized in material. shader should be additive or simple transparent, depends on your task. so background is static image, and at front of it a set of planes that placed in brush way. using scale and alpha you can customize size/power of brush. color is color as is. in performance reason you can do something like 'bake drawing', that will take a brushed image and place it as background with removing all drawed planes.


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class UnbreakablePaint : MonoBehaviour
	public Transform BrushPrefab;
	public Material MaterialForPrefab;
	public Material MaterialForDrawPlane;
	public Camera BakeCamera;
	public RenderTexture TextureUsedInBakeCamera;
	float betweenPointDistance = 0.1f;
	Vector3 lastBrushPosition =;
	bool lastBrushExists = false;
	Material currentMaterial;
	Color materialColor =;
	List brushesToDelete;
	void OnGUI()
		GUI.Label(new Rect(10f, 10f, 10f, 30f), "R");
		GUI.Label(new Rect(10f, 40f, 10f, 30f), "G");
		GUI.Label(new Rect(10f, 70f, 10f, 30f), "B");
		GUI.Label(new Rect(10f, 100f, 10f, 30f), "A");
		materialColor.r = GUI.HorizontalSlider(new Rect(30f, 10f, 100f, 30f), materialColor.r, 0f, 1f);
		materialColor.g = GUI.HorizontalSlider(new Rect(30f, 40f, 100f, 30f), materialColor.g, 0f, 1f);
		materialColor.b = GUI.HorizontalSlider(new Rect(30f, 70f, 100f, 30f), materialColor.b, 0f, 1f);
		materialColor.a = GUI.HorizontalSlider(new Rect(30f, 100f, 100f, 30f), materialColor.a, 0f, 1f);
		GUI.Label(new Rect(10f, Screen.height - 40f, 400f, 30f), "select color and draw with right mouse button");
	void Update()
		if (Input.GetMouseButtonDown(1))
			currentMaterial = new Material(MaterialForPrefab);
			currentMaterial.color = materialColor;
			brushesToDelete = new List();
		if (Input.GetMouseButton(1))
			Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
			Vector3 newBrushPosition = mouseRay.origin - mouseRay.direction / mouseRay.direction.y * mouseRay.origin.y;
			while (!lastBrushExists || (newBrushPosition - lastBrushPosition).sqrMagnitude >= betweenPointDistance * betweenPointDistance)
		if (Input.GetMouseButtonUp(1))
			lastBrushExists = false;
			BakeCamera.Render(); = TextureUsedInBakeCamera;
			Texture2D tex2d = new Texture2D(TextureUsedInBakeCamera.width, TextureUsedInBakeCamera.height, TextureFormat.RGB24, false);
			tex2d.ReadPixels(new Rect(0, 0, TextureUsedInBakeCamera.width, TextureUsedInBakeCamera.height), 0, 0);
			MaterialForDrawPlane.mainTexture = tex2d;
			for (int i = brushesToDelete.Count - 1; i >= 0; i--)
*			}*
*		}*
*	}*
*	void MakePaint(Vector3 pointPaintDirectionTo)*
*	{*
*		Vector3 pointPaintNow = lastBrushExists ? Vector3.MoveTowards(lastBrushPosition, pointPaintDirectionTo, betweenPointDistance) : pointPaintDirectionTo;*
*		Transform brush = Instantiate(BrushPrefab, pointPaintNow, Quaternion.identity) as Transform;*
*		brushesToDelete.Add(brush);*
*		brush.renderer.material = currentMaterial;*
*		lastBrushPosition = pointPaintNow;*
*		lastBrushExists = true;*
*	}*

I figured out how to get rid of the dots. I was on the right track before, I just needed to read from the base texture and write to the buffertexture and apply the buffer texture on mouse up. Derp.