Unity - RenderWithShader, SetReplacementShader, ResetReplacementShader 测试

141 篇文章 34 订阅
74 篇文章 4 订阅

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

Project

TestReplacementShader.zip

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值