关于材质球中渲染光照一些理解

2020-06-02 14:44:04 浏览数 (1)

材质球

Specular Highlights:镜面光 Reflection: 遮罩反射 一个Cubemap 一个texture来描述我们对象的那些部分是可以反射的,而哪些不可以。记住,黑色表示没有任何反射性,而白色表示可以完全反射。下面的图片是我们将会用到的texture

代码语言:javascript复制
Shader "Custom/MaskedReflection" {  
    Properties {  
        _MainTex ("Base (RGB)", 2D) = "white" {}  
        _MainTint ("Diffuse Tint", Color) = (1,1,1,1)  
        _ReflAmount ("Reflection Amount", Range(0, 1)) = 1  
        _Cubemap ("Cubemap", CUBE) = ""{}  
        _ReflMask ("Reflection Mask", 2D) = ""{}  
    }  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
        LOD 200  
          
        CGPROGRAM  
        #pragma surface surf Lambert  
  
        sampler2D _MainTex;  
        sampler2D _ReflMask;  
        samplerCUBE _Cubemap;  
        float4 _MainTint;  
        float _ReflAmount;  
  
        struct Input {  
            float2 uv_MainTex;  
            float3 worldRefl;  
        };  
  
        void surf (Input IN, inout SurfaceOutput o) {  
            half4 c = tex2D (_MainTex, IN.uv_MainTex);  
            float3 reflection = texCUBE(_Cubemap, IN.worldRefl).rgb;  
            float4 reflMask = tex2D(_ReflMask, IN.uv_MainTex);  
      
            o.Albedo = c.rgb * _MainTint;  
            o.Emission = (reflection * reflMask.r) * _ReflAmount;  
            o.Alpha = c.a;  
        }  
        ENDCG  
    }   
    FallBack "Diffuse"  
}  
代码语言:javascript复制
Shader "Custom/AlphaMask" {  
       Properties  
        {  
        _Color ("Main Color", Color) = (1,1,1,1)  
        _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}  
        _MaskTex ("Mask (A)", 2D) = "white" {}  
        _Progress ("Progress", Range(0,1)) = 0.5  
        }  
        Category  
        {  
            Lighting Off  
            ZWrite Off  
            Cull back  
            Fog { Mode Off }  
            Tags {"Queue"="Transparent" "IgnoreProjector"="True"}  
            Blend SrcAlpha OneMinusSrcAlpha  
            SubShader  
            {  
                Pass  
                {  
                    CGPROGRAM  
                    #pragma vertex vert  
                    #pragma fragment frag  
                    sampler2D _MainTex;  
                    sampler2D _MaskTex;  
                    fixed4 _Color;  
                    float _Progress;  
                    struct appdata  
                    {  
                        float4 vertex : POSITION;  
                        float4 texcoord : TEXCOORD0;  
                    };  
                    struct v2f  
                    {  
                        float4 pos : SV_POSITION;  
                        float2 uv : TEXCOORD0;  
                    };  
                    v2f vert (appdata v)  
                    {  
                        v2f o;  
                        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
                        o.uv = v.texcoord.xy;  
                        return o;  
                }  
                    half4 frag(v2f i) : COLOR  
                    {  
                        fixed4 c = _Color * tex2D(_MainTex, i.uv);  
                        fixed ca = tex2D(_MaskTex, i.uv).a;  
                        c.a *= ca >= _Progress ? 0f : 1f;  
                        return c;  
                    }  
                    ENDCG  
                }  
            }  
            SubShader  
            {            
                 AlphaTest LEqual [_Progress]    
                  Pass    
                  {    
                     SetTexture [_MaskTex] {combine texture}    
                     SetTexture [_MainTex] {combine texture, previous}    
                  }    
            }  
              
        }  
        Fallback "Transparent/VertexLit"  
    }  
Forward Rendering

Forward Rendering是绝大数引擎都含有的一种渲染方式。要使用Forward Rendering,一般在Vertex Shader或Fragment Shader阶段对每个顶点或每个像素进行光照计算,并且是对每个光源进行计算产生最终结果。正向渲染一个基于着色器的渲染路径。在Unity中它支持逐像素计算光照(包括法线贴图和灯光Cookies)和来自一个平行光的实时阴影。在默认设置中,少数最亮的灯光在逐像素计算光照模式下渲染。其余的灯光计算对象顶点的光照。 下面是Forward Rendering的核心伪代码。

代码语言:javascript复制
For each light:
  For each object affected by the light:
    framebuffer  = object * light

在Unity3D 引擎中,对于下图中的圆圈(表示一个Geometry),进行Forward Rendering处理。

将得到下面的处理结果

也就是说,对于ABCD四个光源我们在Fragment Shader中我们对每个pixel处理光照,对于DEFG光源我们在Vertex Shader中对每个vertex处理光照,而对于GH光源,我们采用球调和(SH)函数进行处理。 Forward Rendering优缺点

很明显,对于Forward Rendering,光源数量对计算复杂度影响巨大,所以比较适合户外这种光源较少的场景(一般只有太阳光)。

但是对于多光源,我们使用Forward Rendering的效率会极其低下。因为如果在vertex shader中计算光照,其复杂度将是 ,而如果在fragment shader中计算光照,其复杂度为 。可见光源数目和复杂度是成线性增长的。

对此,我们需要进行必要的优化。比如

  • 1.多在vertex shader中进行光照处理,因为有一个几何体有10000个顶点,那么对于n个光源,至少要在vertex shader中计算10000n次。而对于在fragment shader中进行处理,这种消耗会更多,因为对于一个普通的1024x768屏幕,将近有8百万的像素要处理。所以如果顶点数小于像素个数的话,尽量在vertex shader中进行光照。
  • 2.如果要在fragment shader中处理光照,我们大可不必对每个光源进行计算时,把所有像素都对该光源进行处理一次。因为每个光源都有其自己的作用区域。比如点光源的作用区域是一个球体,而平行光的作用区域就是整个空间了。对于不在此光照作用区域的像素就不进行处理。但是这样做的话,CPU端的负担将加重,因为要计算作用区域。
  • 3.对于某个几何体,光源对其作用的程度是不同,所以有些作用程度特别小的光源可以不进行考虑。典型的例子就是Unity中只考虑重要程度最大的4个光源。

自发光

Global Illumination 全局光照设置

  • 自发光效果 很多人都会奇怪,为什么我选了自发光的颜色,强度也调整的很大,为什么出不来效果呢,其实可能是少了一步操作。 首先我们需要先把这个自发光物体,选中LightMap静态,然后再把周围物体也都选中LightMap静态,再进行烘焙即可。很简单的,但遗了一步就会没有任何效果。
  • 镜面 在模拟一个diffuse surface时,使用的是光滑,无光泽的object。这种方法,对于场景中的大部分objects都是合适的,而且很多光照模型都基于此种方法。但是,有时也要模拟闪光的模型表面,比如抛光的金属或者大理石地板的表面。Specular highlights就是用于模拟这些带有光泽的object表面。 有两种方法可以模拟镜面反射。第一种是Phong反射模型中的镜面反射部分,Phong模型以它的发明者,来自Utah大学的Bui Tuong Phong命名。与diffuse shading不同的是,镜面光与观察者(即camera)相对于表面的位置有关。只要看向一个发光的object,就自己观察到这一点,注意观察从不同的方向看向object时,该object的光泽如何改变。Phong模型指出,镜面光由观察方向和光的反射向量之间的夹角确定。公式为:

其中,R为反射向量,V表示观察方向,s表示亮点(照亮的区域)的大小。Specular exponent(镜面指数)越大,产生的亮点越小。反射向量可以使用如下的公式计算:

代码语言:javascript复制
R = 2*(N • L)*N-L

0 人点赞