最近在研究喷涂喷绘项目,需要做大量纹理图形运算,因此更适合用GPU来处理,在unity中用ComputeShader完成像素运算,SurfaceShader完成纹理渲染。
实现思路:
1.用射线碰撞模型,得到碰撞纹理坐标brushX和brushY
2.ComputeShader拿到brush坐标,在纹理相应的位置做像素运算,并输出纹理ComputeTexture
3.通过SurfaceShader渲染纹理
管理代码:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.VersionControl;
using UnityEngine;
using UnityEngine.UI;
namespace Game.Patinter
{
public class PatintManager : MonoBehaviour
{
//图片尺寸
public static int textureSize = 1024;
//渲染模型
public GameObject paintSurfacePanel;
//渲染模型
public GameObject paintDataPanel;
//运算shader
private ComputeShader computeShader;
//运算纹理
private RenderTexture computeTexture;
//输出纹理
private Texture2D resultTexture;
private int computeShaderKernel;
// Start is called before the first frame update
void Start()
{
//实例化shader
computeShader = (ComputeShader) Resources.Load("Shader/PaintComputeShader", typeof(ComputeShader));
computeShaderKernel = computeShader.FindKernel("PaintCompute");
//创建纹理
computeTexture = new RenderTexture(textureSize, textureSize, 24);
computeTexture.enableRandomWrite = true;
computeTexture.Create();
//绑定纹理
computeShader.SetTexture(computeShaderKernel, "paintTexture", computeTexture);
computeShader.SetInt("textureSize", textureSize);
//输出纹理
resultTexture = new Texture2D(textureSize, textureSize, TextureFormat.ARGB32, false);
}
// Update is called once per frame
void Update()
{
//鼠标左键按下开始喷绘
if (Input.GetMouseButton(0))
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Physics.Raycast(ray, out hit, 100);
//检测到碰撞体
if (hit.collider != null)
{
var hitX = hit.textureCoord.x;
var hitY = hit.textureCoord.y;
Render(hitX, hitY);
return;
}
//未检测到碰撞体
Render(0, 0);
}
else
{
Render(0, 0);
}
//空格清空面板
if (Input.GetKeyDown(KeyCode.Space))
{
computeTexture = new RenderTexture(textureSize, textureSize, 24);
computeTexture.enableRandomWrite = true;
computeTexture.Create();
computeShader.SetTexture(computeShaderKernel, "paintTexture", computeTexture);
Render(0, 0);
}
}
void Render(float hitX, float hitY)
{
//传入绘制点
computeShader.SetFloat("brushX", hitX * textureSize);
computeShader.SetFloat("brushY", hitY * textureSize);
//纹理运算
computeShader.Dispatch(computeShaderKernel, textureSize / 8, textureSize / 8, 1);
//拷贝纹理
RenderTexture.active = computeTexture;
resultTexture.ReadPixels(new Rect(0, 0, textureSize, textureSize), 0, 0);
resultTexture.Apply();
//渲染纹理
paintDataPanel.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", resultTexture);
paintSurfacePanel.GetComponent<MeshRenderer>().material.SetTexture("_PaintDataTex", resultTexture);
}
}
}
代码很简单,就80行;在Start中初始化;Update中检测射线实时渲染;Render负责传入参数启动运算。
ComputeShader:
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel PaintCompute
// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> paintTexture;
float brushX;
float brushY;
int textureSize;
[numthreads(8,8,1)]
void PaintCompute(uint3 id : SV_DispatchThreadID)
{
//绘制点有效
if (brushX != 0 && brushY != 0)
{
// TODO: insert actual code here!
//和绘制点距离
float dis = distance(id.xy, float2(brushX, brushY));
//距离小于90个像素
if (dis < 90)
{
float4 v = paintTexture[id.xy];
//添加边缘模糊
float rate = 1 - dis / 90;
v.x += 0.04 * rate;
paintTexture[id.xy] = v;
}
}
//流体效果
if (id.y > 0)
{
float4 v = paintTexture[id.xy];
//大于0.9向下流动
if (v.x > 0.9)
{
v.x -= 0.02f;
paintTexture[id.xy] = v;
float4 down = paintTexture[id.xy - float2(0, 1)];
if (down.x < 0.8f)down.x = 0.8;
down += float4(0.0178f, 0, 0, 0);
paintTexture[id.xy - float2(0, 1)] = down;
}
}
}
为了更像喷绘的效果,需要加上边缘模糊和流体效果
模型shader:
Shader "Custom/PaintSurfaceShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_PaintDataTex ("PaintDataTex (RGB)", 2D) = "black" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _PaintDataTex;
struct Input
{
float2 uv_MainTex;
float3 worldRefl;
INTERNAL_DATA
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf(Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
fixed4 p = tex2D(_PaintDataTex, IN.uv_MainTex);
o.Albedo = c.rgb * (1 - p.r) + _Color * p.r;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
将模型本身的纹理和喷涂上色混合
热力图Shader:
Shader "Custom/PaintDataShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "black" {}
_ColorMapTex ("ColorMap (RGB)", 2D) = "black" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _ColorMapTex;
struct Input
{
float2 uv_MainTex;
float3 worldPos;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf(Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
fixed4 m = tex2D(_ColorMapTex, float2((1 - c.r)*0.9 + 0.09, 0.1));
o.Albedo = m.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
此功能是为了反映喷涂渲染状况
效果:
左边是热力图,右边效果图
源码:https://download.csdn.net/download/u014261855/87777737
后记:流体没有+法线,还有优化空间,有兴趣的朋友可以优化讨论