130 lines
4.4 KiB
C#
Raw Normal View History

2025-09-08 14:51:28 +08:00
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
2025-09-08 17:37:12 +08:00
namespace DongWuYiXue.DaoNiaoShu
2025-09-08 14:51:28 +08:00
{
public class DrawOn3D : MonoBehaviour
{
public Color paintColor = Vector4.one;
[Range(10, 200)] public int paintSize = 60;
[Range(0.2f, 10)] public float paintHardness = 1;
[Range(0, 1.0f)] public float fullRatio = 1f;
public UnityAction<bool> drawFull;
public Texture2D brushTexture;
public Transform mq;
/// <summary>
/// 主摄像机
/// </summary>
private Camera cam;
[SerializeField]
private ComputeShader computer;
[SerializeField]
private ComputeShader clearDraw;
[SerializeField]
private Shader drawShader;
[SerializeField]
private Shader blitShader;
private ComputeBuffer value;
private int[] full = new int[1];
private int[] full2 = new int[1];
private RenderTexture rt2;
private Material mat;
private Material matblit;
private bool isFull = false;
private Mesh mesh;
int limit;
//public GameObject hand1;
//public GameObject hand2;
void Start()
{
cam = Camera.main;
//ComputeBufferType.IndirectArguments认定此buffer大小必须为12字节
value = new ComputeBuffer(1, 4, ComputeBufferType.IndirectArguments);
rt2 = new RenderTexture(512, 512, 8);
rt2.enableRandomWrite = true;
rt2.Create();
mat = new Material(drawShader);
matblit = new Material(blitShader);
mesh = GetComponent<SkinnedMeshRenderer>().sharedMesh;
limit = rt2.width * rt2.height;
}
// 在RenderTexture的(x,y)坐标处画笔刷图案
private void Draw(int x, int y)
{
//绘制rendertexture
RenderTexture.active = rt2;
GL.PushMatrix();
GL.LoadPixelMatrix(0, rt2.width, rt2.height, 0);
// 绘制贴图
x -= (int)(paintSize * 0.5f);
y -= (int)(paintSize * 0.5f);
Rect rect = new Rect(x, y, paintSize, paintSize);//brushTexture.width, brushTexture.height);
mat.SetColor("_BaseColor", paintColor);
mat.SetFloat("_PaintHardness", paintHardness);
Graphics.DrawTexture(rect, brushTexture, mat, 0);
// 弹出改变
GL.PopMatrix();
RenderTexture.active = null;
//检测是否绘制满不用每帧执行,这里隔一帧执行一次
//if (run)
//{
full[0] = 0;
value.SetData(full);
computer.SetTexture(0, "Result", rt2);
computer.SetBuffer(0, "Full", value);
computer.Dispatch(0, rt2.width / 8, rt2.height / 8, 1);
value.GetData(full2);
//}
//run = !run;
//full2等于1表示绘制满等于0表示没有绘制满
if (full2[0] > limit * fullRatio)
{
drawFull?.Invoke(true);
isFull = true;
Debug.Log("已经画满");
}
}
private void Update()
{
if (Physics.Raycast(mq.localPosition, mq.forward, out RaycastHit hit))
{
// hit.textureCoord是碰撞点的uv值uv值是从0到1的所以要乘以宽高才能得到具体坐标点
var x = (int)(hit.textureCoord.x * rt2.width) % rt2.width;
// 注意uv坐标系和Graphics坐标系的y轴方向相反
var y = (int)(rt2.height - hit.textureCoord.y * rt2.height) % rt2.height;
Draw(x, y);
}
//渲染绘制的画面
matblit.SetTexture("_MainTex", rt2);
matblit.SetColor("_BaseColor", paintColor);
Graphics.DrawMesh(mesh, transform.localToWorldMatrix, matblit, 0);
}
public void ClearDraw()
{
clearDraw.SetTexture(0, "Result", rt2);
clearDraw.Dispatch(0, rt2.width / 8, rt2.height / 8, 1);
//清除画板后,画满回调可再次触发
isFull = false;
}
private void OnDestroy()
{
rt2.Release();
value.Release();
rt2 = null;
value = null;
}
}
}