【Unity Shader】渲染纹理实现镜子效果

1 基本概念

1.1 什么是渲染到纹理?

全称是Render To Texture,《入门精要》好像又把渲染目标纹理,即Render Target Texture也叫做RTT,但我认为《入门精要》的RTT更多的是“中间缓冲区”这个缓冲区,而Render To Texture这个RTT更多的是指渲染到纹理这一个操作,为了避免混乱接下来我说的RTT都是指Render To Texture这个渲染操作

在我们学习渲染过程基础的时候,一个Camera的渲染结果通常会说“输出并储存在颜色缓冲中”,再等待最后出现在屏幕上。当然,这也是常规的渲染操作。至于为什么会是渲染到纹理?有时候渲染会想要实现一些特效,那么此时RTT也像常规渲染操作一样渲染一个场景,但这次并不是储存在帧缓冲(双缓冲则是储存在后缓冲)中,而是渲染出一张纹理并在之后进行使用。

关于RTT更专业点的叙述:现代图形处理单元(GPU)允许我们将3D场景先渲染到中间缓冲区中,也就是说不会直接到达默认的帧缓冲或者后缓冲(关于各种缓冲我在【技术美术图形部分】图形渲染管线3.0-光栅化和像素处理阶段末尾有做简单的介绍)。

1.2 什么是渲染纹理?

全称Render Texture,简称RT,也就是上述提到的渲染出的那张纹理,这是Unity为渲染目标纹理专门定义的一种纹理类型。

1.3 RTT的应用范围

上面说了,是为了实现一些特效效果。一般是在游戏的摄像机进行设置,使得摄像机渲染的结果能存到Render Texture中。

1.4 什么是多重渲染目标?

Multiple Render Target, 简称MRT,也叫做多目标渲染,是一个意思。多重渲染目标允许我们同时把场景渲染到多个渲染目标纹理中,不用再是渲染整个场景后才储存。在【技术美术图形部分】关于前向渲染和延迟渲染中我在Unity实现延迟渲染中,提到了“RT0、RT1、RT2、RT3”这四个渲染纹理,就是延迟渲染使用到MRT的体现。

2 模拟镜子效果

2.1 原理

就是创建除了MainCamera之外的另一个Camera:用于渲染镜子想要反射的场景,同时这个Camera的Render Target选择创建的一个Render Texture,这样的话选中的Camera渲染的场景将不会到屏幕上,而是存到的选中的渲染纹理RT中。同时要注意,既然是镜像效果,画面肯定是要反转一下

2.2 效果展示

这里我为了练习自己搜索纹理的能力没有用《入门精要》提供的一些纹理图片,而是在网上搜索(推荐一个免费的纹理贴图网站:Texture Ninja,如果有更好的欢迎分享!)自己想要效果的纹理图片当作Texture,然后在PS里获得Texuture的法线图,再分别给场景中的Wall、Sphere、Cube们赋予想要的材质。 

2.3 实现过程

2.3.1 准备场景

首先是搭建场景,创建6个立方体把MainCamera包围住,场景中放置一些物体,再加上3个Point Light,赋予对应的材质,Shader用的是标准光照Shader。

2.3.2 创建RT/Camera/“镜子”

Project视图下右键,Create -> Render Texture创建一个MirrorTexture渲染纹理;

扫描二维码关注公众号,回复: 14540370 查看本文章

Scene视图下右键,创建一个Camera,并将Render Target选中刚才创建的RT:

我们的镜子就用一个Quad平面代替,还需要创建MirrorMat材质以及MirrorShader。

2.3.3 用于“镜子”平面的Shader

这个Shader输入的texture是刚才创建的RenderTexuture,整个结构非常简单,这里就直接贴上代码了:

Shader "Unity Shaders Book/Chapter 10/Mirror"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            sampler2D _MainTex;

            struct a2v {
                //必不可少的,object->clipspace
                float4 vertex : POSITION;
                //要使用uv?那要先存着
                float4 texcoord : TEXCOORD0; 
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0; 
            };

            v2f vert(a2v  v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord; //什么都不需要做,就是获取个纹理而已
                //还需要左右翻转一下,关于x=0.5对称:
                o.uv.x = 1 - o.uv.x;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                return tex2D(_MainTex, i.uv);
            }
            ENDCG
        }
    }
    FallBack  OFF
}

2.4 特点及应用

2.4.1 “镜子”清晰度与什么有关?

清晰度完全取决于这个Render Texture的分辨率,上述效果中我的分辨率选择的是默认的256x256,看上去会比较模糊,如果改成1024x1024,则会清晰很多,但会消耗更多的性能,必要时注意清晰度和性能二者的取舍:

2.4.2 应用

游戏里的镜子是怎么做出来的? - 知乎 (zhihu.com)

想了解更多可以自行搜索“游戏中怎么做镜子效果”等等,这里我只是贴了一个知乎的问题,16年的时候就有人回答了用渲染纹理实现镜子效果的方法。

猜你喜欢

转载自blog.csdn.net/qq_41835314/article/details/127407801