视频链接
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
public class PaintManager : Singleton<PaintManager>
{
public Shader texturePaint;
private Material paintMaterial;
int debugUVID = Shader.PropertyToID("_DebugUV");
int positionID = Shader.PropertyToID("_Position");
int hardnessID = Shader.PropertyToID("_Hardness");
int strengthID = Shader.PropertyToID("_Strength");
int radiusID = Shader.PropertyToID("_Radius");
int colorID = Shader.PropertyToID("_Color");
int textureID = Shader.PropertyToID("_MainTex");
CommandBuffer command;
private void Awake()
{
paintMaterial = new Material(texturePaint);
command = new CommandBuffer();
command.name = "CommmandBuffer - " + gameObject.name;
}
public void InitUVMask(Paintable paintable)
{
RenderTexture mask = paintable.GetMask();
RenderTexture copy = paintable.GetCopy();
Renderer rend = paintable.GetRenderer();
paintMaterial.SetInt(debugUVID, 1);
command.SetRenderTarget(mask);
command.SetRenderTarget(copy);
command.DrawRenderer(rend, paintMaterial, 0);
Graphics.ExecuteCommandBuffer(command);
command.Clear();
}
public void Paint(Paintable paintable, Vector3 pos, float radius = 1f, float hardness = .5f, float strength = .5f, Color? color = null)
{
RenderTexture mask = paintable.GetMask();
RenderTexture copy = paintable.GetCopy();
Renderer rend = paintable.GetRenderer();
paintMaterial.SetInt(debugUVID, 0);
paintMaterial.SetVector(positionID, pos);
paintMaterial.SetColor(colorID, color ?? Color.red);
paintMaterial.SetFloat(hardnessID, hardness);
paintMaterial.SetFloat(strengthID, strength);
paintMaterial.SetFloat(radiusID, radius);
paintMaterial.SetTexture(textureID, copy);
command.SetRenderTarget(mask);
command.DrawRenderer(rend, paintMaterial, 0);
command.SetRenderTarget(copy);
command.Blit(mask, copy);
Graphics.ExecuteCommandBuffer(command);
command.Clear();
}
}
挂给想绘制的物体:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Paintable : MonoBehaviour
{
public bool debugUV = true;
public int textureSize = 1024;
private Renderer rend;
private RenderTexture maskTexture;
private RenderTexture copyTexture;
private int maskTextureID = Shader.PropertyToID("_MaskTex");
public Renderer GetRenderer() => rend;
public RenderTexture GetMask() => maskTexture;
public RenderTexture GetCopy() => copyTexture;
void Start()
{
maskTexture = new RenderTexture(textureSize, textureSize, 0);
maskTexture.filterMode = FilterMode.Bilinear;
copyTexture = new RenderTexture(textureSize, textureSize, 0);
copyTexture.filterMode = FilterMode.Bilinear;
rend = GetComponent<Renderer>();
rend.material.SetTexture(maskTextureID, copyTexture);
if (debugUV)
{
PaintManager.Instance.InitUVMask(this);
}
}
void OnDisable()
{
maskTexture.Release();
copyTexture.Release();
}
}
绘制的Shader:
Shader "LSQ/EffectAchievement/Paint/TexturePainter"
{
SubShader
{
Cull Off ZWrite Off ZTest Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float3 _Position;
float4 _Color;
float _Radius;
float _Hardness;
float _Strength;
int _DebugUV;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 worldPos : TEXCOORD1;
};
float mask(float3 position, float3 center, float radius, float hardness)
{
float m = distance(center, position);
return 1 - smoothstep(radius * hardness, radius, m);
}
v2f vert (appdata v)
{
v2f o;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = v.uv;
float4 uv = float4(0, 0, 0, 1);
uv.xy = float2(1, _ProjectionParams.x) * (v.uv.xy * 2 - 1);
o.vertex = uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
if (_DebugUV)
{
return 1;
}
float4 col = tex2D(_MainTex, i.uv);
float f = mask(i.worldPos, _Position, _Radius, _Hardness);
float edge = f * _Strength;
return lerp(col, _Color, edge);
}
ENDCG
}
}
}
被绘制的物体的Shader
Shader "LSQ/EffectAchievement/Paint/Paintable"
{
Properties
{
[Head(Render)]
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_BumpMap ("Normal", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
[Head(Paint)]
_MaskTex ("Mask (RGB)", 2D) = "black" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _MaskTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
fixed4 mask = tex2D (_MaskTex, IN.uv_MainTex);
fixed value = max(mask.r, max(mask.g, mask.b));
o.Albedo = lerp(c.rgb, mask.rgb, value);
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
绘制:
public abstract class Painter : MonoBehaviour
{
public Camera cam;
[Space]
public bool mouseSingleClick;
[Space]
public Color paintColor;
[Min(0.5f)]
public float radius = 1;
public float strength = 1;
public float hardness = 1;
}
用鼠标绘制:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MousePainter : Painter
{
void Update()
{
bool click;
click = mouseSingleClick ? Input.GetMouseButtonDown(0) : Input.GetMouseButton(0);
if (click)
{
Vector3 position = Input.mousePosition;
Ray ray = cam.ScreenPointToRay(position);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100.0f))
{
Debug.DrawRay(ray.origin, hit.point - ray.origin, Color.red);
transform.position = hit.point;
Paintable p = hit.collider.GetComponent<Paintable>();
if (p != null)
{
PaintManager.Instance.Paint(p, hit.point, radius, hardness, strength, paintColor);
}
}
}
}
}
用粒子绘制:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ParticlesPainter : Painter
{
public float minRadius = 0.05f;
public float maxRadius = 0.2f;
[Space]
ParticleSystem part;
List<ParticleCollisionEvent> collisionEvents;
void Start(){
part = GetComponent<ParticleSystem>();
collisionEvents = new List<ParticleCollisionEvent>();
}
void OnParticleCollision(GameObject other)
{
int numCollisionEvents = part.GetCollisionEvents(other, collisionEvents);
Paintable p = other.GetComponent<Paintable>();
if (p != null)
{
for (int i = 0; i < numCollisionEvents; i++)
{
Vector3 pos = collisionEvents[i].intersection;
float radius = Random.Range(minRadius, maxRadius);
PaintManager.Instance.Paint(p, pos, radius, hardness, strength, paintColor);
}
}
}
}
|