【Shader】在unity中实现笔刷功能

效果:

教程链接:传送门:)​​​​​​ 

原理

基本

unityshader可以对材质贴图进行编辑,在在片元着色器中检测到顶点BrushPos小于BrushSize的顶点颜色修改位BrushColor。

笔尖位置发出一条射线,检测碰撞点,将点渲染到贴图上(将笔刷有关信息传入shader)

保存上一帧、当前帧、画布材质,在三者之间进行切换更新。

优化

移速过快会造成点阵距离过大不够流畅,于是在相距大于BrushSize的两个点之间进行插值补足空隙。

Shader

Shader "Brush/MarkPenEffect"
{
	properties{
		_MainTex("Texture",2D) = "white"{}
		_BrushPos("BrushPos",Vector) = (0,0,0,0)
		_BrushColor("Brush Color",Color) = (1,1,1,1)
		_BrushSize("Brush Size",float) = 0.01
	}

		Subshader{
			Tags{"RenderType" = "Opaque"}
			pass {
			CGPROGRAM
				#pragma vertex vert;
				#pragma fragment frag;
				#include "UnityCg.cginc"

				struct appdata {
					float4 vertex:POSITION;
					float2 uv:TEXCOORD0;
				};

				struct v2f {
					float2 uv:TEXCOORD0;
					float4 vertex:SV_POSITION;
				};
				sampler2D _MainTex;

				float4  _BrushPos;
				float4 _BrushColor;
				float _BrushSize;

				v2f vert(appdata v) {
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv = v.uv;
					return o;
				}

				fixed4 frag(v2f i) :SV_Target{
					fixed4 col = tex2D(_MainTex,i.uv);
					if (length(i.uv - _BrushPos.xy) < _BrushSize) {
						col = _BrushColor;
					}
					return col;
				}
				ENDCG
			}
		}
}

脚本代码

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class MarkPen : MonoBehaviour
{
    private Texture tex;//这张图是板子的原图
    public RenderTexture cacheTex;//缓存上一帧的图
    RenderTexture currentTex;//当前帧操作的图


    public float brushSize = 0.01f;
    public Color brushCol = Color.red;

    private Material effectMat;//用来处理图像的材质

    private Material renderMat;//原始面板的材质

    public Transform penHead;
    public Transform board;
    private Vector2 lastuv;

    private bool isDown;
    // Start is called before the first frame update
    void Start()
    {
        Initialized();
    }

    // Update is called once per frame
    void Update()
    {
        Ray ray = new Ray(penHead.position, transform.forward);
        if(Physics.Raycast(ray,out RaycastHit raycastHit, 1, LayerMask.GetMask("Board")))
        {

            if (!isDown)
            {
                isDown = true;
                lastuv = raycastHit.textureCoord2;
            }
            RenderBrushToBoard(raycastHit);
            lastuv = raycastHit.textureCoord2;
        }
        else
        {
            isDown= false; 
        }

    }

    private void RenderBrushToBoard(RaycastHit hit)
    {
        Debug.Log("1");
        Vector2 dir = hit.textureCoord2 - lastuv;

        if (Vector3.SqrMagnitude(dir) > brushSize * brushSize)
        {
            int length = Mathf.CeilToInt(dir.magnitude / brushSize);

            for (int i = 0; i < length; i++)
            {
                RenderToMatTex(lastuv + dir.normalized * i * brushSize);

            }
        }
        else
        {
            RenderToMatTex(hit.textureCoord2);
        }
        Debug.Log("2");
    }

    /*    private void RenderBrushToBoard(RaycastHit hit)
        {
            RenderToMatTex(hit.textureCoord2);
        }*/

    private void RenderToMatTex(Vector2 uv)
    {
        effectMat.SetVector("_BrushPos",new Vector4(uv.x,uv.y,lastuv.x,lastuv.y));
        effectMat.SetColor("_BrushColor",brushCol);
        effectMat.SetFloat("_BrushSize", brushSize);
        Graphics.Blit(cacheTex,currentTex,effectMat);
        renderMat.SetTexture("_MainText",currentTex);
        Graphics.Blit(currentTex,cacheTex);
    }

    private void Initialized()
    {
        effectMat = new Material(Shader.Find("Brush/MarkPenEffect"));
        Material boardMat = board.GetComponent<MeshRenderer>().material;
        tex =boardMat.mainTexture;

        renderMat = boardMat;

        cacheTex = new RenderTexture(tex.width,tex.height,0,RenderTextureFormat.ARGB32);
        Graphics.Blit(tex,cacheTex);
        renderMat.SetTexture("_MainTex",cacheTex);
        currentTex= new RenderTexture(tex.width, tex.height, 0, RenderTextureFormat.ARGB32);

    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值