- Code
- CSharp
- Shader
- Common.cginc
- A.shader
- B.shader
- C.shader
- ReplaceShader.shader
- ReplaceShader2.shader
- 运行效果
- 总结
- Project
这里做记录用。。。
为了制作其他的一些效果,不得不拿 CommandBuffer
实验一些功能。
这里的替换渲染类似:Camera.RenderWithShader/SetReplacementShader
:Unity - RenderWithShader, SetReplacementShader, ResetReplacementShader 测试
先准备好脚本
Code CSharpusing System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using static CmdBuff_ReplacementRender;
// jave.lin 2020.04.13 - CommandBuffer Replace Render
public class CmdReplaceScript : MonoBehaviour
{
public bool enabledReplace = true;
public Camera cam;
public Shader replaceShader;
private RenderTexture rt;
[SerializeField] private CmdBuff_ReplacementRender replacementCmdBuff;
private void Start()
{
replacementCmdBuff = new CmdBuff_ReplacementRender(cam, replaceShader);
}
private void OnPreRender()
{
if (rt == null || rt.width != Screen.width || rt.height != Screen.height)
{
if (rt) RenderTexture.ReleaseTemporary(rt);
rt = RenderTexture.GetTemporary(Screen.width, Screen.height);
rt.name = "replaceShaderRT";
}
if (enabledReplace) replacementCmdBuff.Update(null, CameraEvent.AfterImageEffects);
else replacementCmdBuff.Clear();
}
private void OnDestroy()
{
if (rt) RenderTexture.ReleaseTemporary(rt);
if (replacementCmdBuff != null)
{
replacementCmdBuff.Destroy();
replacementCmdBuff = null;
}
}
}
// jave.lin 2020.04.13 - CmdBuff_ReplacementRender
[Serializable]
public class CmdBuff_ReplacementRender
{
public enum ReplaceType // 替换类型
{
CheckType, CheckTypeAndValue, IngoreType_ForAll
}
public enum ReplaceTag // 要替换的tag
{
CustomType, All
}
private static void GetRenders(CmdBuff_ReplacementRender replacement, List replaceList, List sourceList)
{
var checkTag = replacement.replaceTag.ToString();
var checkValue = replacement.replaceType == ReplaceType.CheckTypeAndValue ? replacement.replaceMat.GetTag(checkTag, false) : string.Empty;
var renderArr = GameObject.FindObjectsOfType();
if (replacement.replaceType == ReplaceType.IngoreType_ForAll)
{
replaceList.AddRange(renderArr);
}
else if (replacement.replaceType == ReplaceType.CheckType)
{
foreach (var item in renderArr)
{
if (!string.IsNullOrEmpty(item.sharedMaterial.GetTag(checkTag, false)))
{
replaceList.Add(item);
}
else
{
sourceList.Add(item);
}
}
}
else if (replacement.replaceType == ReplaceType.CheckTypeAndValue)
{
foreach (var item in renderArr)
{
if (item.sharedMaterial.GetTag(checkTag, false) == checkValue)
{
replaceList.Add(item);
}
else
{
sourceList.Add(item);
}
}
}
}
public ReplaceType replaceType;
public ReplaceTag replaceTag;
public bool DrawSrcRender = true;
private Camera cam;
private CommandBuffer cmdBuff;
private Shader replaceShader;
[SerializeField] private Material replaceMat;
private CameraEvent lastCamEvent;
[SerializeField] private List replaceList = new List();
[SerializeField] private List sourceList = new List();
public CmdBuff_ReplacementRender(Camera cam, Shader replaceShader = null)
{
this.cam = cam;
this.replaceShader = replaceShader;
this.replaceMat = new Material(this.replaceShader);
}
public void Clear()
{
if (cmdBuff != null) cmdBuff.Clear();
}
public void Update(RenderTexture rt, CameraEvent camEvent)
{
if (this.lastCamEvent != camEvent)
{
if (cmdBuff != null)
{
cam.RemoveCommandBuffer(this.lastCamEvent, cmdBuff);
cmdBuff.name = $"{camEvent} - Draw Replace";
cam.AddCommandBuffer(camEvent, cmdBuff);
}
this.lastCamEvent = camEvent;
}
if (cmdBuff == null)
{
cmdBuff = new CommandBuffer();
cmdBuff.name = $"{this.lastCamEvent} - Draw Replace";
cam.AddCommandBuffer(this.lastCamEvent, cmdBuff);
}
cmdBuff.Clear();
if (rt) cmdBuff.SetRenderTarget(rt);
else cmdBuff.SetRenderTarget(cam.targetTexture);
cmdBuff.ClearRenderTarget(true, true, Color.white);
replaceList.Clear();
sourceList.Clear();
// 如果GetRenders处理了:
// - Camera.cullMask 的筛选
// - Renderer.sortingLayerID 的排序
// - Material.queueID 的排序
// - 等等其他相关的排序,那么就和Camera.RenderWithShader或是SetReplacementShader差不多了
GetRenders(this, replaceList, sourceList);
if (DrawSrcRender)
{
foreach (var r in sourceList)
{
cmdBuff.DrawRenderer(r, r.sharedMaterial);
}
}
foreach (var r in replaceList)
{
cmdBuff.DrawRenderer(r, replaceMat);
}
}
public void Destroy()
{
if (cmdBuff != null && cam != null)
{
cam.RemoveCommandBuffer(this.lastCamEvent, cmdBuff);
cmdBuff = null;
cam = null;
}
if (replaceList != null)
{
replaceList.Clear();
replaceList = null;
}
}
}
Shader
Common.cginc
// jave.lin 2020.04.13
#include "UnityCG.cginc"
#include"Lighting.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
fixed4 _MainColor;
fixed4 _XRayColor;
fixed4 _XRayRimColor;
v2f vert (appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag_common(v2f i, fixed4 tintColor) {
i.worldNormal = normalize(i.worldNormal);
//viewDir后面高光用
half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
// ambient
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
// diffuse
fixed LdotN = dot(lightDir, i.worldNormal);
fixed diffuse = max(0, LdotN);
// specular
fixed specular = 0;
if (LdotN > 0) {
half3 hDir = normalize(viewDir + lightDir);
fixed HdotN = max(0, dot(hDir, i.worldNormal));
specular = pow(HdotN, 64);
}
fixed4 combinedCol = 0;
combinedCol.xyz = ambient +
diffuse * _LightColor0.rgb * tintColor.rgb +
specular * _LightColor0.rgb;
combinedCol.a = tintColor.a;
return combinedCol;
}
fixed4 frag (v2f i) : SV_Target {
return frag_common(i, _MainColor);
}
fixed4 frag_replace (v2f i) : SV_Target {
return frag_common(i, _XRayColor);
}
fixed4 frag_xray (v2f i) : SV_Target {
fixed4 result = frag_common(i, _XRayColor);
// rim
half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
float VdotN = 1 - max(0, dot(i.worldNormal, viewDir));
result.xyz = lerp(result.xyz, _XRayRimColor.rgb, VdotN);
return result;
}
fixed4 frag_replace1(v2f i ) : SV_Target {
return frag_common(i, lerp(_MainColor, _XRayColor, abs(sin(_Time.w))));
}
A.shader
// jave.lin 2020.04.13
Shader "Custom/A" {
Properties {
_MainColor ("Color", Color) = (1,0,0,1)
}
SubShader {
Tags { "CustomType"="A" "All"="1" }
Pass {
CGPROGRAM
#include "Common.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
B.shader
// jave.lin 2020.04.13
Shader "Custom/B" {
Properties {
_MainColor ("Color", Color) = (0,1,0,1)
}
SubShader {
Tags { "CustomType"="B" "All"="1" }
Pass {
CGPROGRAM
#include "Common.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
C.shader
// jave.lin 2020.04.13
Shader "Custom/C" {
Properties {
_MainColor ("Color", Color) = (0,0,1,1)
}
SubShader {
Pass {
CGPROGRAM
#include "Common.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
ReplaceShader.shader
// jave.lin 2020.04.13
Shader "Custom/ReplaceShader" {
Properties {
_MainColor ("Color", Color) = (1,1,0,1)
_XRayColor ("XRayColor", Color) = (0, 1, 1, 0.5)
_XRayRimColor ("XRayRimColor", Color) = (1, 0, 0, 1)
}
SubShader {
Tags { "CustomType"="B" "All"="1" }
Blend SrcAlpha OneMinusSrcAlpha
Pass { // front
ZWrite Off
ZTest Greater
CGPROGRAM
#include "Common.cginc"
#pragma vertex vert
#pragma fragment frag_xray
ENDCG
}
Pass { // front
ZWrite Off
CGPROGRAM
#include "Common.cginc"
#pragma vertex vert
#pragma fragment frag_replace
ENDCG
}
}
}
ReplaceShader2.shader
// jave.lin 2020.04.13
Shader "Custom/ReplaceShader2" {
Properties {
_MainColor ("Color", Color) = (1,1,0,1)
_XRayColor ("XRayColor", Color) = (0, 1, 1, 0.5)
_XRayRimColor ("XRayRimColor", Color) = (1, 0, 0, 1)
}
SubShader {
Tags { "CustomType"="B" "All"="1" }
Blend SrcAlpha OneMinusSrcAlpha
Pass { // front
ZWrite Off
ZTest Greater
CGPROGRAM
#include "Common.cginc"
#pragma vertex vert
#pragma fragment frag_xray
ENDCG
}
Pass { // front
ZWrite Off
CGPROGRAM
#include "Common.cginc"
#pragma vertex vert
#pragma fragment frag_replace1
ENDCG
}
}
}
运行效果
其中
public enum ReplaceType // 替换类型
{
CheckType, CheckTypeAndValue, IngoreType_ForAll
}
有三种类型
CheckType
只要有对应的tag
在,不论之是什么,都可以替换CheckTypeAndValue
要有对应tag
同时,要需要值与替换shader的是一致才替换IngoreType_ForAll
不换检测类似,对所有对象都替换
相比之下,Unity内置的Camera.RenderWithShader
接口只能处理 tag
对 value
都相同才能替换。
如果GetRenders
处理了:
Camera.cullMask
的筛选Renderer.sortingLayerID
的排序Material.queueID
的排序- 等等其他相关的排序
那么就和Camera.RenderWithShader
或是SetReplacementShader
差不多了
限制: 想让Renderer
指定在其他的Camera
下渲染都不行 如:CommandBuffer.DrawRenderer(Renderer, Material);
中的Renderer
是相对该CommadnBuffer
绑定的Camera
的变换关系的。
我还想用一个不启用(Active == false
)的Camera
来作为CommandBuffer
的渲染事件来绑定又不可以,因为没有启用(Active == false
)的Camera
是不会调用 CameraEvent
的。
反正CommandBuffer
使用起来还是会有很多限制。
所以,Unity下替换渲染妥妥的还是用回Camera
来处理吧。
backup : Unity_CmdBuffReplacementRender_2018.3.0f2
有个更完善的 CmdBuff_ReplacementRender 累,在之前一篇文章:Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap