找回密码
 立即注册
查看: 425|回复: 0

Unity-Shader 07 高级纹理 镜面与玻璃

[复制链接]
发表于 2022-5-28 17:02 | 显示全部楼层 |阅读模式
资源与声明:

1-本文章部分图片来源 UCSB 闫令琪 老师《GAMES101 现代计算机图形学入门》
https://sites.cs.ucsb.edu/~lingqi/teaching/games101.html
2-本文章部分图片来源 UCSB 闫令琪 老师《GAMES202 高质量实时渲染》
https://sites.cs.ucsb.edu/~lingqi/teaching/games202.html
3-本文章代码来源 冯乐乐老师 《Unity Shader入门精要》
https://github.com/candycat1992/Unity_Shaders_Book/tree/unity_2017_1
4-本文章部分图片与素材来源 Unity Shader入门精要学习笔记
Unity Shader入门精要学习笔记:高级纹理
// 如有侵权 欢迎联系删除
// 若文章有误,欢迎指正讨论
1- 立方体纹理

1-1 天空盒子

天空盒是在所有不透明物体之后渲染的,而背后使用的网格是一个立方体或者是一个细分后的球体。
可以自己定义一个立方体;



6 sides


Light Setting - environment 来自定义我们的 环境光
天空盒是在所有不透明物体之后渲染的;
1-2 用于创建环境映射的立方体纹理

第一种方法
是直接由一些特殊布局的纹理创建。
我们只需要把纹理的Texture Type设置为Cubemap即可。可以对纹理数据进行压缩,而且可以支持边缘修正,光滑反射(glossy reflection)和HDR功能。
第二种方法
就是直接把6张图甩给Cubemap;
第三种方法
是由脚本生成。我们往往希望根据物体在场景中位置的不同,生成他们各自不同的立方体纹理。
这可以通过Unity提供的Camera.RenderToCubemap实现,Camera.RenderToCubemap可以把从任意位置观察到的场景图像存储到6张图像中,从而创建出该位置上对应的立方体纹理。
就是在任意一个位置创建一个临时摄像机,然后记录6个方向的位置,最后再销毁摄像机;
using UnityEngine;
using UnityEditor;
using System.Collections;

public class RenderCubemapWizard : ScriptableWizard {
       
        public Transform renderFromPosition;
        public Cubemap cubemap;
       
        void OnWizardUpdate () {
                helpString = "Select transform to render from and cubemap to render into";
                isValid = (renderFromPosition != null) && (cubemap != null);
        }
       
        void OnWizardCreate () {
                // create temporary camera for rendering
                GameObject go = new GameObject( "CubemapCamera");
                go.AddComponent<Camera>();
                // place it on the object
                go.transform.position = renderFromPosition.position;
                // render into cubemap               
                go.GetComponent<Camera>().RenderToCubemap(cubemap);
               
                // destroy temporary camera
                DestroyImmediate( go );
        }
       
        [MenuItem("GameObject/Render into Cubemap")]
        static void RenderCubemap () {
                ScriptableWizard.DisplayWizard<RenderCubemapWizard>(
                        "Render cubemap", "Render!");
        }
}
1-3 反射

使用了反射效果的物体看起来像是镀了一层金属,想要模拟反射效果很简单,我们只需要通过入射光线的方向和表面法线方向来计算反射方向,再利用反射方向对立方体纹理采样即可。


// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 10/Reflection" {
        Properties {
                _Color ("Color Tint", Color) = (1, 1, 1, 1)
                _ReflectColor ("Reflection Color", Color) = (1, 1, 1, 1)  // 控制反射颜色
                _ReflectAmount ("Reflect Amount", Range(0, 1)) = 1 //控制反射程度
                _Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}  //模拟反射的环境模拟映射
        }
        SubShader {
                Tags { "RenderType"="Opaque" "Queue"="Geometry"}
               
                Pass {
                        Tags { "LightMode"="ForwardBase" }
                       
                        CGPROGRAM
                       
                        #pragma multi_compile_fwdbase
                       
                        #pragma vertex vert
                        #pragma fragment frag
                       
                        #include "Lighting.cginc"
                        #include "AutoLight.cginc"
                       
                        fixed4 _Color;
                        fixed4 _ReflectColor;
                        fixed _ReflectAmount;
                        samplerCUBE _Cubemap;
                       
                        struct a2v {
                                float4 vertex : POSITION;
                                float3 normal : NORMAL;
                        };
                       
                        struct v2f {
                                float4 pos : SV_POSITION;
                                float3 worldPos : TEXCOORD0;
                                fixed3 worldNormal : TEXCOORD1;
                                fixed3 worldViewDir : TEXCOORD2;
                                fixed3 worldRefl : TEXCOORD3;
                                SHADOW_COORDS(4)
                        };
                       
                        v2f vert(a2v v) {
                                v2f o;
                               
                                o.pos = UnityObjectToClipPos(v.vertex);
                               
                                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                               
                                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                               
                                o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
                               
                                // 计算世界坐标下的反射方向
                                o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
                               
                                TRANSFER_SHADOW(o);
                               
                                return o;
                        }
                       
                        fixed4 frag(v2f i) : SV_Target {
                                fixed3 worldNormal = normalize(i.worldNormal);
                                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));               
                                fixed3 worldViewDir = normalize(i.worldViewDir);               
                               
                                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                               
                                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));
                               
                                // 用反射方向向Cubemap 采样
                                fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;
                               
                                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                               
                                // Mix the diffuse color with the reflected color
                                // 将漫反射的颜色和Cubemap反射的颜色混合
                                // 也有考虑光照衰减
                                fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount) * atten;
                               
                                return fixed4(color, 1.0);
                        }
                       
                        ENDCG
                }
        }
        FallBack "Reflective/VertexLit"
}

最关键是用反射方向向Cubemap 采样
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;1-4 折射

这个海平面的波光粼粼,就相当于有很多放大镜。聚焦
我们可以规定很多折射率,是sinθ的系数! 我们可以通过折射率,通过入射角算出出射角;



钻石的折射率是2.42
就是为什么钻石看起来闪闪发光


就是 用cos的根号,会保证在入射角大于折射角的时候,才一定有理!!
要是这个cos求出来不有理呢,就是只有在密度低往密度高的方向走才会发生。
像在水里看外面。


全反射现象
我们在水底,智能看到一个锥形的区域内有光,其他地方都没有光,因为我们只有在97.2度内看到的视角,才不会被折射回水底。


我们还能自己定义折射率;
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 10/Refraction" {
        Properties {
                _Color ("Color Tint", Color) = (1, 1, 1, 1)
                _RefractColor ("Refraction Color", Color) = (1, 1, 1, 1)
                _RefractAmount ("Refraction Amount", Range(0, 1)) = 1
                _RefractRatio ("Refraction Ratio", Range(0.1, 1)) = 0.5 //折射率
                _Cubemap ("Refraction Cubemap", Cube) = "_Skybox" {}
        }
        SubShader {
                Tags { "RenderType"="Opaque" "Queue"="Geometry"}
               
                Pass {
                        Tags { "LightMode"="ForwardBase" }
               
                        CGPROGRAM
                       
                        #pragma multi_compile_fwdbase       
                       
                        #pragma vertex vert
                        #pragma fragment frag
                       
                        #include "Lighting.cginc"
                        #include "AutoLight.cginc"
                       
                        fixed4 _Color;
                        fixed4 _RefractColor;
                        float _RefractAmount;
                        fixed _RefractRatio; // 折射率
                        samplerCUBE _Cubemap;
                       
                        struct a2v {
                                float4 vertex : POSITION;
                                float3 normal : NORMAL;
                        };
                       
                        struct v2f {
                                float4 pos : SV_POSITION;
                                float3 worldPos : TEXCOORD0;
                                fixed3 worldNormal : TEXCOORD1;
                                fixed3 worldViewDir : TEXCOORD2;
                                fixed3 worldRefr : TEXCOORD3;
                                SHADOW_COORDS(4)
                        };
                       
                        v2f vert(a2v v) {
                                v2f o;
                                o.pos = UnityObjectToClipPos(v.vertex);
                               
                                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                               
                                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                               
                                o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
                               
                                // 计算世界空间下的反射率
                                // 参数:光线入射方向,世界空间下的法线,还有折射率
                                o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);
                               
                                TRANSFER_SHADOW(o);
                               
                                return o;
                        }
                       
                        fixed4 frag(v2f i) : SV_Target {
                                fixed3 worldNormal = normalize(i.worldNormal);
                                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                                fixed3 worldViewDir = normalize(i.worldViewDir);
                                                               
                                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                               
                                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));
                               
                                // 用折射方向向Cubemap 采样
                                fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;
                               
                                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                               
                                // Mix the diffuse color with the reflected color
                                // 将漫反射的颜色和Cubemap反射的颜色混合
                                // 也有考虑光照衰减
                                fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;
                               
                                return fixed4(color, 1.0);
                        }
                       
                        ENDCG
                }
        }
        FallBack "Reflective/VertexLit"
}

关键是用折射方向对Cubemap 采样
1-5 菲涅尔反射

菲涅尔项!


越是水平看,反射效果越明显
(绝缘体)



绝缘体

这个意思:
要是入射光和法线平行,那么大部分会折射(小部分反射),要是入射光和法线垂直,那么只有少部分会折射(大部分反射)。
就像,坐车的试试,看前面的车窗,是反射的,看自己的是通透的!!
极化现象,和光线的波动性有关,意思是只沿着某一个方向波动!
but 金属(导体),及时垂直看过去,反射率也很大;



金属(导体)

菲涅尔项的公式很复杂


两个极化,给平均一下;
Schlick's 近似
不管多复杂,都和入射光,和材质有关系。
简化方法
R0 是FresnelScale



Schlick's 近似

代码关键是用Schlick's 近似计算菲涅尔
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 10/Fresnel" {
        Properties {
                _Color ("Color Tint", Color) = (1, 1, 1, 1)
                _FresnelScale ("Fresnel Scale", Range(0, 1)) = 0.5 //用Fresnel 控制菲涅尔的程度
                _Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}
        }
        SubShader {
                Tags { "RenderType"="Opaque" "Queue"="Geometry"}
               
                Pass {
                        Tags { "LightMode"="ForwardBase" }
               
                        CGPROGRAM
                       
                        #pragma multi_compile_fwdbase
                       
                        #pragma vertex vert
                        #pragma fragment frag
                       
                        #include "Lighting.cginc"
                        #include "AutoLight.cginc"
                       
                        fixed4 _Color;
                        fixed _FresnelScale; //用Fresnel 控制菲涅尔的程度
                        samplerCUBE _Cubemap;
                       
                        struct a2v {
                                float4 vertex : POSITION;
                                float3 normal : NORMAL;
                        };
                       
                        struct v2f {
                                float4 pos : SV_POSITION;
                                float3 worldPos : TEXCOORD0;
                                  fixed3 worldNormal : TEXCOORD1;
                                  fixed3 worldViewDir : TEXCOORD2;
                                  fixed3 worldRefl : TEXCOORD3;
                                 SHADOW_COORDS(4)
                        };
                       
                        v2f vert(a2v v) {
                                v2f o;
                                o.pos = UnityObjectToClipPos(v.vertex);
                               
                                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                               
                                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                               
                                o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
                               
                                o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
                               
                                TRANSFER_SHADOW(o);
                               
                                return o;
                        }
                       
                        fixed4 frag(v2f i) : SV_Target {
                                fixed3 worldNormal = normalize(i.worldNormal);
                                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                                fixed3 worldViewDir = normalize(i.worldViewDir);
                               
                                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                               
                                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                               
                                // 用反射光方向进行采样
                                fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb;
                               
                                // 用Schlick's approximation计算的;
                                // 计算菲涅尔项
                                fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);
                                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));
                               
                                // 光照的混合
                                fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;
                               
                                return fixed4(color, 1.0);
                        }
                       
                        ENDCG
                }
        }
        FallBack "Reflective/VertexLit"
}




具有边缘光照效果的漫反射物体;

2- 渲染纹理

一个摄像机的渲染结果会输出到颜色缓冲中,并显示到我们的屏幕上。
现代的GPU允许我们把整个三维场景渲染到一个中间缓冲中,即渲染目标纹理(Render Target Texture,RTT),而不是传统的帧缓冲或后备缓冲(back buffer)。与之相关的是多重渲染目标纹理(Multiple Render Target,MRT)这种技术指的是GPU允许我们把场景同时渲染到多个渲染目标纹理中,而不再需要为每个渲染目标纹理单独渲染完整的场景。
延迟渲染就是使用多重渲染目标的一个应用。
Unity为渲染目标纹理定义了一种专门的纹理类型——渲染纹理(Render Texture)
使用方式有二
1-直接创建一个渲染纹理,然后把某个摄像机的渲染目标设置成该渲染纹理。(对应镜子)
2-在屏幕后处理时使用GrabPass命令或OnRenderImage函数来获取当前屏幕图像,Unity会把这个屏幕图像放到一张和屏幕分辨率等同的渲染纹理中,下面我们可以在自定义Pass中把他们当成普通的纹理来处理,从而实现各种屏幕特效。(对应玻璃)
大概意思就是,GPU会把Camera看到的整个三维场景,渲染成一个纹理或者添加到纹理里面;
2-1 镜子效果(渲染纹理)

创建一个shader,用于左右反转uv,创建一个Render Texture,用以接收摄像机传来的纹理,额外创建一个摄像机,用以向Render Texture传递纹理。


所谓镜子,就是把背面的一个Camera 看到的当做是一个Render Texture 赋予给镜子物体;
侧面看会露馅,不是一个镜子,只是一个camera看到的Texture







可以看到Mirror Texture;


直接把 Mirror Texture 拖到 Camera属性的Target Texture上,这样Camera看到的就会存在这个Mirror Texture上;
我们还需要调整相机的视角;
要是迷糊不清的话,就调整texture的尺寸

这个shader代码其实不重要,就是把左右给Flip一下,是简单的纹理代码
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 10/Mirror" {
        Properties {
                _MainTex ("Main Tex", 2D) = "white" {} //只有一个Render Texture
        }
        SubShader {
                Tags { "RenderType"="Opaque" "Queue"="Geometry"}
               
                Pass {
                        CGPROGRAM
                       
                        #pragma vertex vert
                        #pragma fragment frag
                       
                        sampler2D _MainTex;
                       
                        struct a2v {
                                float4 vertex : POSITION;
                                float3 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;
                                // Mirror needs to filp x
                                // 只是把采样点得到的纹理坐标给filp了一下
                                o.uv.x = 1 - o.uv.x;
                               
                                return o;
                        }
                       
                        fixed4 frag(v2f i) : SV_Target {
                                return tex2D(_MainTex, i.uv);
                        }
                       
                        ENDCG
                }
        }
        FallBack Off
}

2-2 玻璃效果(GrabPass)

在Shader中定义一个GrabPass后,Unity会把当前屏幕的图像绘制在一张纹理中,以便我们在后续的Pass中继续访问它。
使用GrabPass可以让我们对该物体后面的图像进行更加复杂的处理,例如使用法线来模拟折射效果,而不再是简单的和原屏幕颜色进行混合。
在使用GrabPass时,我们需要额外小心物体的渲染队列设置。GrabPass通常用于渲染透明物体,尽管代码里并不包含混合指令,但我们往往仍然需要把物体的渲染队列设置成透明队列(“Queue” = “Transparent”)。这样才可以保证当渲染该物体时,所有不透明物体都已经被绘制到屏幕上。
大体思路:首先使用一张法线纹理来修改模型的法线信息,然后通过一个Cubemap来模拟玻璃的反射,而在模拟折射时,则使用了GrabPass获取玻璃后的屏幕图像,并使用切线空间下的法线
重点:GrabPass抓取的屏幕图像,是用于折射的,因为并不知道透明的Cube背后是啥,预期再去渲染不如用之前Grab抓取到的图像;


GrabPass支持两种形式
直接使用GrabPass{},然后在后续的Pass中直接使用_GrabTexture来访问屏幕图像。但是如果场景中多个物体都使用这个形式,会造成较大性能消耗。因为对于每一个使用它的物体,Unity都会为它单独进行一次昂贵的屏幕抓取操作。
使用GrabPass{“TextureName”},可以在后续的Pass中使用TextureName来访问屏幕图像。Unity只会在每一帧时为第一个使用名为TextureName的纹理的物体进行一次抓取屏幕,同样可以在其他Pass中被访问。即所有使用该命令的物体都是同样的屏幕图像。
怎么有点像预先截图?

代码的重点是,GrabPass 得到玻璃体之外的屏幕截图,并应用到折射上;
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 10/Glass Refraction"
{
        Properties
        {
                //玻璃的材质纹理
                _MainTex("Main Tex", 2D) = "white" {}
        //玻璃的法线纹理
        _BumpMap("Normal Map", 2D) = "bump" {}
        //用于模拟反射的环境纹理
        _Cubemap("Environment Cubemap", Cube) = "_Skybox" {}
        //控制模拟折射时图像的扭曲程度
        _Distortion("Distortion", Range(0, 100)) = 10
                //控制折射程度
                _RefractAmount("Refract Amount", Range(0.0, 1.0)) = 1.0
        }
                SubShader
        {
                //渲染队列设置成Transparent保证物体渲染时,其他不透明物体已经被渲染到屏幕上了
                //渲染类型设置为不透明,使用着色器替换时,该物体可以在需要时被正确渲染。
                //这通常发生在我们需要得到摄像机的深度和法线贴图时
                Tags { "Queue" = "Transparent" "RenderType" = "Opaque" }

                //获取屏幕图像存到名为_RefractionTex纹理中
                GrabPass { "_RefractionTex" }

                Pass
                {
                        CGPROGRAM

                        #pragma vertex vert
                        #pragma fragment frag

                        #include "UnityCG.cginc"

                        sampler2D _MainTex;
                        float4 _MainTex_ST;
                        sampler2D _BumpMap;
                        float4 _BumpMap_ST;
                        samplerCUBE _Cubemap;
                        float _Distortion;
                        fixed _RefractAmount;
                        sampler2D _RefractionTex;
                        //纹素大小
                        //一个266X512的纹理,它的纹素是(1/256,1/512)
                        //我们需要在对屏幕图像采样坐标进行偏移时使用该变量
                        float4 _RefractionTex_TexelSize;

                        struct a2v
                        {
                                float4 vertex : POSITION;
                                float3 normal : NORMAL;
                                float4 tangent : TANGENT;
                                float2 texcoord: TEXCOORD0;
                        };

                        struct v2f
                        {
                                float4 pos : SV_POSITION;
                                float4 scrPos : TEXCOORD0;
                                float4 uv : TEXCOORD1;
                                float4 TtoW0 : TEXCOORD2;
                                float4 TtoW1 : TEXCOORD3;
                                float4 TtoW2 : TEXCOORD4;
                        };

                        v2f vert(a2v v)
                        {
                                v2f o;
                                o.pos = UnityObjectToClipPos(v.vertex);
                                //得到被抓取的屏幕图像的采样坐标
                                o.scrPos = ComputeGrabScreenPos(o.pos);
                                //计算_MainTex和_BumpMap的采样坐标
                                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
                                o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
                                //切线空间到世界空间变换矩阵,把每一行分别存在TtoW0,TtoW1,TtoW2中
                                float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                                //计算世界空间下顶点切线
                                fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
                                //计算世界空间下顶点副切线
                                fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
                                //计算世界空间下顶点法线
                                fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
                                //w分量是世界空间下顶点位置(充分利用寄存器空间)
                                o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                                o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                                o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

                                return o;
                        }

                        fixed4 frag(v2f i) : SV_Target
                        {
                                //解译我们的顶点世界坐标
                                float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
                                //得到该片元对应的视角方向
                                fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));

                                //对法线纹理进行采样,得到切线空间下的法线方向
                                fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));

                                //对屏幕图像采样坐标进行偏移,模拟折射效果
                                float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;

                                i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
                                //对抓取的屏幕图像_RefractionTex进行采样,得到模拟的折射颜色
                                //进行透视除法,得到真正的屏幕坐标
                                fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;

                                //法线方向从切线空间转世界空间
                                bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
                                //视角方向相对于法线方向的反射方向
                                fixed3 reflDir = reflect(-worldViewDir, bump);
                                //对主纹理进行采样
                                fixed4 texColor = tex2D(_MainTex, i.uv.xy);
                                //对Cubemap进行采样,把结果和主纹理颜色相乘后得到反射颜色
                                fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;
                                //反射颜色和折射颜色进行混合,作为最终输出颜色
                                fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;

                                return fixed4(finalColor, 1);
                        }

                        ENDCG
                }
        }

                FallBack "Diffuse"
}

2-3 渲染纹理 & GrabPass

渲染纹理就是镜子那个,用一个Camera 看到的作为Target Texture;
GrabPass, 就是玻璃效果,直接给截个图;
从效率上来说,渲染纹理效率往往要好于GrabPass,使用渲染纹理我们可以自定义渲染纹理的大小。而使用GrabPass获取到的屏幕分辨率和显示屏幕是一致的,这意味着在一些高分辨率设备上可能会造成严重的带宽影响。
在移动设备上,GrabPass虽然不会重新渲染场景,但它往往需要CPU直接读取后备缓冲(back buffer),破坏了CPU和GPU并行性,这往往是比较耗时的。一些移动设备不支持。
Unity引入了命令缓冲(Command Buffers)来允许我们拓展Unity的渲染流水线。
3- 程序纹理

3-1 Unity中简单的程序纹理

简单说,就是把纹理给参数化了;
程序纹理是指那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化团或非常真实的自然元素,例如木头,石子。
好处是我们可以通过各种参数来控制纹理的外观,而这些属性不仅仅是那些衍射属性,甚至可以是完全不同类型的图案属性。
代码的重点是,自定义图像的参数化;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[ExecuteInEditMode]  // 为了能在编辑器模式下运行
public class ProceduralTextureGeneration : MonoBehaviour {

        public Material material = null;

        // 定义纹理的属性
        #region Material properties
        [SerializeField, SetProperty("textureWidth")] //纹理的大小
        private int m_textureWidth = 512;
        public int textureWidth {
                get {
                        return m_textureWidth;
                }
                set {
                        m_textureWidth = value;
                        _UpdateMaterial();
                }
        }

        [SerializeField, SetProperty("backgroundColor")] //背景颜色
        private Color m_backgroundColor = Color.white;
        public Color backgroundColor {
                get {
                        return m_backgroundColor;
                }
                set {
                        m_backgroundColor = value;
                        _UpdateMaterial();
                }
        }

        [SerializeField, SetProperty("circleColor")] //原点大小
        private Color m_circleColor = Color.yellow;
        public Color circleColor {
                get {
                        return m_circleColor;
                }
                set {
                        m_circleColor = value;
                        _UpdateMaterial();
                }
        }

        [SerializeField, SetProperty("blurFactor")] //模糊因子(用来模糊原型边界的)
        private float m_blurFactor = 2.0f;
        public float blurFactor {
                get {
                        return m_blurFactor;
                }
                set {
                        m_blurFactor = value;
                        _UpdateMaterial();
                }
        }
        #endregion

        private Texture2D m_generatedTexture = null;

        // Use this for initialization
        void Start () {
                if (material == null) {
                        Renderer renderer = gameObject.GetComponent<Renderer>();
                        if (renderer == null) {
                                Debug.LogWarning("Cannot find a renderer.");
                                return;
                        }

                        material = renderer.sharedMaterial;
                }

                _UpdateMaterial();
        }

        private void _UpdateMaterial() {
                if (material != null) {
                        m_generatedTexture = _GenerateProceduralTexture();
                        material.SetTexture("_MainTex", m_generatedTexture);
                }
        }

        /// <summary>
        /// 颜色混合,模糊边界
        /// </summary>
        /// <param name="color0"></param>
        /// <param name="color1"></param>
        /// <param name="mixFactor"></param>
        /// <returns></returns>
        private Color _MixColor(Color color0, Color color1, float mixFactor) {
                Color mixColor = Color.white;
                mixColor.r = Mathf.Lerp(color0.r, color1.r, mixFactor);
                mixColor.g = Mathf.Lerp(color0.g, color1.g, mixFactor);
                mixColor.b = Mathf.Lerp(color0.b, color1.b, mixFactor);
                mixColor.a = Mathf.Lerp(color0.a, color1.a, mixFactor);
                return mixColor;
        }
        /// <summary>
        /// 生成纹理
        /// </summary>
        /// <returns></returns>
        private Texture2D _GenerateProceduralTexture() {
                Texture2D proceduralTexture = new Texture2D(textureWidth, textureWidth);

                // 圆边距
                float circleInterval = textureWidth / 4.0f;
                // 圆半径
                float radius = textureWidth / 10.0f;
                // 模糊因子
                float edgeBlur = 1.0f / blurFactor;

                // 用两个for 来遍历每个像素

                for (int w = 0; w < textureWidth; w++) {
                        for (int h = 0; h < textureWidth; h++) {

                                // 背景颜色初始化
                                Color pixel = backgroundColor;

                                // 画圆
                                for (int i = 0; i < 3; i++) {
                                        for (int j = 0; j < 3; j++) {
                                                // 计算当前绘制圆的圆心位置
                                                Vector2 circleCenter = new Vector2(circleInterval * (i + 1), circleInterval * (j + 1));

                                                // 计算当前像素与圆心的距离
                                                float dist = Vector2.Distance(new Vector2(w, h), circleCenter) - radius;

                                                // 模糊圆的边界
                                                Color color = _MixColor(circleColor, new Color(pixel.r, pixel.g, pixel.b, 0.0f), Mathf.SmoothStep(0f, 1.0f, dist * edgeBlur));

                                                // 与之前的颜色进行混合
                                                pixel = _MixColor(pixel, color, color.a);
                                        }
                                }

                                proceduralTexture.SetPixel(w, h, pixel);
                        }
                }

                proceduralTexture.Apply();

                return proceduralTexture;
        }
}



3-2 Unity的程序材质

使用Substance Sesigner制作。 就是材质的参数化;
但是程序材质这个功能以及被移除了,需要下一个插件 Substance

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 06:35 , Processed in 0.093866 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表