在Unity中實時繪制紋理總共需要兩個東西, 一個是RenderTexture, 而如何確定繪制的區(qū)域則需要用到Raycast到MeshCollider從而獲取到指定的textureCoord, 然后使用GL調(diào)用底層函數(shù)把一個Material繪制在RenderTexture上.
需要注意的是:一個有效的可繪制紋理模型,它的Mesh必須有有效的uv數(shù)據(jù),而Unity自帶的各個預(yù)制模型是沒有正常的UV數(shù)據(jù),從而無法在它們身上繪制紋理.在試驗的使用請不要使用類似Unity自帶類似Cube,Sphere等模型.*
第一步: 準(zhǔn)備
定義一個畫筆紋理, 比如一個白色32x32的圓.
畫筆紋理
然后為畫筆紋理創(chuàng)建一個Shader:
Shader "Painting"
{
Properties
{
_MainTex("MainTex (RGB) Trans (A)", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Cull Off
Lighting Off
ZWrite Off
Fog{ Mode Off }
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
fixed4 _Color;
v2f vert(appdata_base IN)
{
v2f OUT;
OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
OUT.texcoord = IN.texcoord;
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
float4 col = _Color * tex2D(_MainTex, IN.texcoord);
col.rgb *= col.a;
return col;
}
ENDCG
}
}
}
最后創(chuàng)建一個畫筆Material, 將紋理和shader應(yīng)用上. 最終成為這樣
畫筆材質(zhì)
被畫的模型也應(yīng)當(dāng)應(yīng)用一個至少有一個Texture參數(shù)的Shader,比如Unlit/Texture
,這樣我們才能夠?qū)⑺瓉淼募y理替換成我們在運行時創(chuàng)建的RenderTexture.
場景搭建完畢以后是這樣:
Game視圖
被繪制的模型的Inspector窗口
第二步: 編寫繪畫代碼
一切盡在代碼中
using UnityEngine;
public class Painter : MonoBehaviour
{
public const int SIZE = 512;
public Material mat;
public float scale;
public Color col;
private Renderer rend;
private RenderTexture rendTex;
private Texture brushTex;
private void Start()
{
//使用ARGB32的格式創(chuàng)建一個RenderTexture
rendTex = new RenderTexture(SIZE, SIZE, 24, RenderTextureFormat.ARGB32);
rendTex.isPowerOfTwo = true;
rendTex.useMipMap = false;
rendTex.Create();
//清空畫布
Clear();
//獲取當(dāng)前模型的Renderer把它的紋理替換成剛建立的RenderTexture
rend = GetComponent<Renderer>();
rend.material.SetTexture("_MainTex", rendTex);
//獲取畫筆紋理
brushTex = mat.mainTexture;
//設(shè)置我們想要的顏色到畫筆材質(zhì)上.
mat.SetColor("_Color", col);
}
private void Update()
{
//當(dāng)鼠標(biāo)按下時發(fā)射射線碰撞模型
if (Input.GetMouseButton(0))
{
var mp = Input.mousePosition;
var ray = Camera.main.ScreenPointToRay(mp);
RaycastHit rayHit;
if (Physics.Raycast(ray, out rayHit))
{
//uv坐標(biāo)是0~1,而我們要的是它紋理上的坐標(biāo)是0~SIZE,于是乘以紋理的SIZE
DrawBrush((int)(rayHit.textureCoord.x * SIZE), (int)(rayHit.textureCoord.y * SIZE), col, scale);
}
}
}
private void Clear()
{
Graphics.SetRenderTarget(rendTex);
GL.PushMatrix();
GL.Clear(true, true, Color.white);
GL.PopMatrix();
}
private void DrawBrush(int x, int y, Color col, float scale)
{
//計算畫筆居中當(dāng)前位置以后的四個角的坐標(biāo)
var left = x - brushTex.width * scale / 2f;
var right = x + brushTex.width * scale / 2f;
var top = y + brushTex.height * scale / 2f;
var bot = y - brushTex.height * scale / 2f;
//將GPU的繪制目標(biāo)轉(zhuǎn)移到當(dāng)前RenderTexture上
Graphics.SetRenderTarget(rendTex);
//使用GL圖像庫繪制一個四邊形
GL.PushMatrix();
GL.LoadOrtho();
mat.SetPass(0);
GL.Begin(GL.QUADS);
GL.TexCoord2(0, 0);
GL.Vertex3(left / SIZE, bot / SIZE, 0);
GL.TexCoord2(0, 1);
GL.Vertex3(left / SIZE, top / SIZE, 0);
GL.TexCoord2(1, 1);
GL.Vertex3(right / SIZE, top / SIZE, 0);
GL.TexCoord2(1, 0);
GL.Vertex3(right / SIZE, bot / SIZE, 0);
GL.End();
GL.PopMatrix();
//搞定
}
}
最后將Painter腳本添加到剛才的模型身上, 運行游戲,點擊鼠標(biāo)左鍵就能在模型上畫畫了.是不是很溜!!!