文章目录
Unity - RenderWithShader, SetReplacementShader, ResetReplacementShader 测试
最不喜欢介绍API,但可以参考该API的设计思路,想想,如果你自己先的引擎中,需要这些功能时,方可参考设计,当然如果不写引擎,就当作是磨砺一下使用的Unity这个工具。
API 简介
这三个API都是在Camera类下的
- RenderWithShader(Shader replaceShader, strign replaceTags)
- 调用后就会立刻按参数替换对应渲染对象使用的shader,再调用底层渲染管线
- 第一个参数是传入替换为你想使用的replaceShader(所有替换成功的都会使用replaceShader来渲染)
- 第二个参数是替换对应带有tags属性中带有你传递的replaceTags的名称的属性,如:Tags{ “P1”=“V1” },如果你想替换带有"P1"的属性,那么replaceTags就传入:"P1"即可,对应的,如果你的replaceShader中没有P1或是你想替换的shader中,都没有P1 Tags属性的shader,那么将不会渲染内容,具体讲解,在下面会讲到
- SetReplacementShader(Shader replaceShader, strign replaceTags)
- 调用后,该Camera需要渲染的对象的shader(带有replaceTags属性的shader),直到调用ResetReplacementShader前,都会使用replaceShader中,替换成功的SubShader来渲染,相当于持久性的RenderWithShader的替换功能,但不会有立刻渲染的功能。
- ResetReplacementShdaer()
- 恢复渲染对象使用Shader(在SetReplacementShader(Shader replaceShader, strign replaceTags)之前的shader)
Demo
先准备脚本
Shader
- BeforeRenderReplaceCOM.cginc
- Cube.shader
- Sphere.shader
- Capsule.shader
- ReplacementShaderUnlitColor.shader
Scripts
- TestCamReplacementShader.cs
Shader
分别代码如下:
BeforeRenderReplaceCOM.cginc
// BeforeRenderReplaceCOM.cginc
// jave.lin 2019.08.29
#include "UnityCG.cginc"
// vert、frag是未Replace前使用的shader
float4 vert (float4 vertex : POSITION) : SV_POSITION { return UnityObjectToClipPos(vertex); }
fixed4 frag () : SV_Target { return fixed4(1,1,1,1); }
Cube.shader
// Cube.shader
// jave.lin 2019.08.29
Shader "Test/Cube" {
CGINCLUDE
#include "BeforeRenderReplaceCOM.cginc"
ENDCG
SubShader {
Tags { "RenderType"="Opaque" "Color"="Red" "RenderWithShader"="Test" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
Fallback "Diffuse"
}
Sphere.shader
// Sphere.shader
// jave.lin 2019.08.29
Shader "Test/Sphere" {
CGINCLUDE
#include "BeforeRenderReplaceCOM.cginc"
ENDCG
SubShader {
Tags { "RenderType"="Opaque" "Color"="Green" "RenderWithShader"="Test" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
Fallback "Diffuse"
}
Capsule.shader
// Capsule.shader
// jave.lin 2019.08.29
Shader "Test/Capsule" {
CGINCLUDE
#include "BeforeRenderReplaceCOM.cginc"
ENDCG
SubShader {
Tags { "RenderType"="Opaque" "Color"="Blue" "RenderWithShader"="Test" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
Fallback "Diffuse"
}
ReplacementShaderUnlitColor.shader
// jave.lin 2019.08.29
// 用于替换Cube, Sphere, Capsule的shader
Shader "Test/ReplacementShaderUnlitColor" {
Properties {
}
CGINCLUDE
#include "UnityCG.cginc"
// SetReplacementShader的shader
float4 vert (float4 vertex : POSITION) : SV_POSITION { return UnityObjectToClipPos(vertex); }
fixed4 frag_Red () : SV_Target { return fixed4(1,0,0,1); }
fixed4 frag_Green () : SV_Target { return fixed4(0,1,0,1); }
fixed4 frag_Blue () : SV_Target { return fixed4(0,0,1,1); }
// RenderWithShader的shader
//与vert函数一样void vert_renderWithShader(float4 vertex : POSITION) : SV_POSITION { return UnityObjectToClipPos(vertex); }
fixed4 frag_renderWithShader () : SV_Target { return fixed4(1,1,0,1); }
ENDCG
SubShader {
Tags { "Color"="Red" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_Red
ENDCG
}
}
SubShader {
Tags { "Color"="Green" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_Green
ENDCG
}
}
SubShader {
Tags { "Color"="Blue" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_Blue
ENDCG
}
}
SubShader {
Tags { "RenderWithShader"="Test" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_renderWithShader
ENDCG
}
}
Fallback "Diffuse"
}
CSharp Scripts
// jave.lin 2019.08.29
using System.Collections;
using UnityEngine;
public class TestCamReplacementShader : MonoBehaviour
{
public Shader replaceShader;
// 公开给外部Inspector中双击RT可查看内容
public RenderTexture replaceColorRT;
void Start()
{
if (replaceColorRT == null || replaceColorRT.width != Screen.width || replaceColorRT.height != Screen.height)
{
replaceColorRT = RenderTexture.GetTemporary(Screen.width, Screen.height);
}
var cam = GetComponent<Camera>();
var srcTT = cam.targetTexture;
var srcClearFlags = cam.clearFlags;
var srcBgColor = cam.backgroundColor;
// 测试RenderWithShader
// RenderWithShader是调用时就会去执行替换,并渲染相机的内容
cam.backgroundColor = Color.black;
cam.clearFlags = CameraClearFlags.Color;
cam.targetTexture = replaceColorRT; // 渲染目标到rt
cam.RenderWithShader(replaceShader, "RenderWithShader");// 立刻渲染
cam.targetTexture = srcTT; // 取消渲染目标
cam.clearFlags = srcClearFlags;
cam.backgroundColor = srcBgColor;
// 仅替换SubShader中Tags带有Color属性定义的
// 且replaceShader中也得有Color属性的Tags,否则啥都不显示,因为没有可使用的SubShader
// 或是替换之前的渲染用的所有对象使用的shader中,也没有一个Color属性的SubShader,也不会显示任何内容
// 假设:replaceShader中有3个subShader,Tags中的Color分别为:"1","2","3",3个
// 如果替换之前的所有对象渲染用的shader中的subshader有对应Color属性的,且Color值为1、2、3之一,都会替换成,否则该替换失败的shader就不会显示
cam.SetReplacementShader(replaceShader, "Color");
// 替换所有渲染时用的shader
//GetComponent<Camera>().SetReplacementShader(replaceShader, null);
StartCoroutine(DelayResetReplacementShader(3, cam)); // 3秒后恢复
}
private IEnumerator DelayResetReplacementShader(float s, Camera cam)
{
yield return new WaitForSeconds(s);
// 恢复的API
cam.ResetReplacementShader();
}
}
Scene - 场景内容
简单放置:
- Cube
- 材质的shader使用:Cube.shader
- Sphere
- 材质的shader使用:Sphere.shader
- Capsule
- 材质的shader使用:Capsule.shader
- Cylinder
- 材质的shader使用:standard(内置的shader)
- Plane
- 材质的shader使用:standard(内置的shader)
在主相机(Main Camera)添加:TestCamReplacementShader.cs脚本组件
对TestCamReplacementShader.cs组件的属性replaceShader设置为:ReplacementShaderUnlitColor.shader
运行效果
详细解析
SetReplacementShader与ResetReplacementShader
Cube中的shader的Color属性是Red,对应替换了ReplacementShaderUnlitColor.shader中的Color=Red的SubShader
Sphere 是Green
Capsule 是Blue
都替换了对应的SubShader来渲染
而:Cylinder、Plane都没有渲染出来,因为没有替换成功。找不到对应的Color Tags属性的SubShader。
然后写了一个3秒后恢复替换前的shader,所以我们可以看到3秒后恢复了渲染效果
RenderWithShader
然后,在主相机(Main Camera)中的TestCamReplacementShader.cs组件中的:replaceColorRT属性对象,我们对该对象鼠标双击,可以看到,如下效果图:
可以看到替换为黄色的对象
而RenderWithShader是再改脚本组件的Start()函数中调用的。
因为RenderWithShader调用后会立刻替换shader,并调用底层渲染管线来渲染。
而渲染前,我们将Camera.targetTexture设置为TestCamReplacementShader.cs脚本组件中的replaceColorRT属性,所以渲染目标就渲染到该RT上了。
合理使用,这几个API可以制作出很多特殊的效果
坑点
有一段时间我想用RenderWithShader来替换一个Shader中,多个SubShader中的其中一个SubShader,发现只能替换第一个兼容的SubShader,什么意思呢?
就是如果我一个Shader里有3个SubShader。
如果我第二个SubShader有tag {“ReplaceTag”=…}的标记,第一个和第三个都没有。
然后运行时使用的是第一个SubShader。
这时我在外部Camera.RenderWithShader(myShader, “ReplaceTag”);
结果替换不了那个在第二个SubShader中带有"ReplaceTag"标记的shader。
我真的是醉了,这么常用的功能竟然没有封装。
Unity论坛上在2013年就有一个人和我的需求是一样的。
结果这个帖子没有一个人问题,帧的是醉了。
Unity 帖子:Multiple calls to renderWithShader