How to render a colored 2D rectangle.

Hi-

I am new to Unity and trying to get a handle on some of the basic rendering functions, most of my experience is with C++ and OpenGL.

To start with I would like to render simple colored rectangles in 2d screen space, I've gotten as far as the following C# code (in OnGUI()):

Texture2D red_texture = new Texture2D(2, 2); Color red_color = new Color(1, 0, 0); red_texture.SetPixel(0, 0, red_color); red_texture.SetPixel(1, 0, red_color); red_texture.SetPixel(1, 1, red_color); red_texture.SetPixel(0, 1, red_color); red_texture.Apply(); red_texture.wrapMode = TextureWrapMode.Repeat; GUI.Box (new Rect (0,0,100,100), red_texture);

(I can create the texture elsewhere, I just included it here for clarity).

This renders a fullsized box with default GUI style, and the texture is shown at literal size as a 2x2 small red square where the text would normally appear.

I tried drawing the rectangle at the exact dimensions of the texture (2x2) and it shows up black, I assume that GUI.Box is not meant for general purpose rendering of colored rectangles.

I also tried the following code in OnDrawGizmos():

Texture2D green_texture = new Texture2D(2, 2); Color green_color = new Color(0, 1, 0); green_texture.SetPixel(0, 0, green_color); green_texture.SetPixel(1, 0, green_color); green_texture.SetPixel(1, 1, green_color); green_texture.SetPixel(0, 1, green_color); green_texture.Apply(); green_texture.wrapMode = TextureWrapMode.Repeat; Gizmos.DrawGUITexture(new Rect(100, 100, 200, 200), green_texture);

This doesn't show up at all (I believe I am missing something).


I couldn't find an example of this in the docs or the forums, I'm assuming this is such an easy operation that it doesn't need to be described in detail anywhere but I am stuck at this point, I would appreciate any push in the right direction.

Thanks in advance for any help -MM


UPDATE:

I managed to get a GUI-box with no border by using a default GUIStyle for the box rendering, but the downside is the texture image has to be the exact size of the desired rectangle, here is the code:

void Render_Colored_Rectangle(int x, int y, int w, int h, float r, float g, float b)
{
    Texture2D rgb_texture = new Texture2D(w, h);
    Color rgb_color = new Color(r, g, b);
    int i, j;
    for(i = 0;i<w;i++)
    {
        for(j = 0;j<h;j++)
        {
            rgb_texture.SetPixel(i, j, rgb_color);
        }
    }
    rgb_texture.Apply();
    GUIStyle generic_style = new GUIStyle();
    GUI.skin.box = generic_style;
    GUI.Box (new Rect (x,y,w,h), rgb_texture);
}

(The display seems to mangle the code in the originalpost but this looks ok)..

This works but is major overkill for a generic rectangle-drawing routine, can anyone clarify the correct way of drawing a basic colored rectangle?

I can use this for now but it's definitely a bad hack, I believe I'm missing something and I haven't found a clear solution in the docs, thanks again for anyone that can point me in the right direction..

There’s no need to create the whole texture (as of Unity 4). This works as expected:

11197-2013-05-20_041131.png

void DrawQuad(Rect position, Color color) {
	Texture2D texture = new Texture2D(1, 1);
	texture.SetPixel(0,0,color);
	texture.Apply();
	GUI.skin.box.normal.background = texture;
	GUI.Box(position, GUIContent.none);
}

Here’s one way to do it with GL.Begin/GL.End. You should also be able to draw a mesh similarly with Graphics.DrawMeshNow. Please select a material that accepts color. I just tested this quickly with GUI/Text Shader but you will probably want to use a solid color shader that uses the vertex color channel.

using UnityEngine;
using System.Collections;

public class Example : MonoBehaviour
{
    // Please assign a material that is using position and color.
    public Material material;
    
    public Rect position = new Rect (16, 16, 128, 24);
    public Color color = Color.red;
    
    void OnGUI ()
    {        
        DrawRectangle (position, color);        
    }
        
    void DrawRectangle (Rect position, Color color)
    {    
        // We shouldn't draw until we are told to do so.
        if (Event.current.type != EventType.Repaint)
            return;
        
        // Please assign a material that is using position and color.
        if (material == null) {
            Debug.LogError ("You have forgot to set a material.");
            return;
        }
        
        material.SetPass (0);
        
        // Optimization hint: 
        // Consider Graphics.DrawMeshNow
        GL.Color (color);
        GL.Begin (GL.QUADS);
        GL.Vertex3 (position.x, position.y, 0);
        GL.Vertex3 (position.x + position.width, position.y, 0);
        GL.Vertex3 (position.x + position.width, position.y + position.height, 0);
        GL.Vertex3 (position.x, position.y + position.height, 0);
        GL.End ();
    }
}

Create a new folder in your Assets and name it "Resources", put a white texture of any size (32x32 for example) inside and name it WhiteTexture (It can be BMP, TGA, PNG, JPEG...) and do something like this in OnGUI function:

Texture2D MyTexture = Resources.Load("WhiteTexture") as Texture2D;
GUI.color = new Color(1.0f , 0, 0);//Set color to red
GUI.DrawTexture(new Rect(positionX, positionY, sizeX, sizeY), MyTexture);
GUI.color = Color.white;//Reset color to white

This will render a red rectangle.

p.s. I didn't test this code. =)

I found a way to draw a box inside OnGUI that only uses a very small texture, and it works on my machine (mac laptop).

I use this to render a 50% transparent white box on the entire screen, including the GUI elements. Just substitute the color values for whatever color you need. You could even make gradients by setting different colors in this 2 by 2 pixel texture.

 void OnGui () {
    Texture2D tex = new Texture2D(2,2);
	for(int i = 0; i < tex.width; i++)
    {
        for(int j = 0; j < tex.height; j++)
        {
            tex.SetPixel(i, j, new Color(1f, 1f, 1f, 0.5f));
        }
    }
	tex.Apply();

	GUIStyle tempstyle = new GUIStyle();
	tempstyle.normal.background = tex;
	GUILayout.BeginArea(new Rect(0,0,Screen.width,Screen.height),tempstyle);
	GUILayout.EndArea();
 }

If someone steps through Veehmot DrawQuad function, as it was said by dchen05 it creates a memory leak. Anyway I found that code useful and I improved it to fix the leak.
This is the new one:

List<KeyValuePair<Color, Texture2D>> quadTextures = new List<KeyValuePair<Color,Texture2D>>();
    void DrawQuad(Rect position, Color color)
    {
        var tmp = quadTextures.Find(t => t.Key == color);
        Texture2D texture;
        if (tmp.Equals(default(KeyValuePair<Color, Texture2D>)))
        {
            texture = new Texture2D(1, 1);
            texture.SetPixel(0, 0, color);
            texture.Apply();
            quadTextures.Add(new KeyValuePair<Color, Texture2D>(color, texture));
        }
        else
        {
            texture = tmp.Value;
        }

        GUI.skin.box.normal.background = texture;
        GUI.Box(position, GUIContent.none);
    }

It may probably be further improved, but it gives the idea on how to overcome the problem :slight_smile:

Drawing a rectangle outline

public static void DrawRectangle(Rect area, int frameWidth, Color color)
{
    //Create a one pixel texture with the right color
    var texture = new Texture2D(1, 1);
    texture.SetPixel(0, 0, color);
    texture.Apply();

    Rect lineArea = area;
    lineArea.height = frameWidth; //Top line
    GUI.DrawTexture(lineArea, texture);
    lineArea.y = area.yMax - frameWidth; //Bottom
    GUI.DrawTexture(lineArea, texture);
    lineArea = area;
    lineArea.width = frameWidth; //Left
    GUI.DrawTexture(lineArea, texture);
    lineArea.x = area.xMax - frameWidth;//Right
    GUI.DrawTexture(lineArea, texture);
}