8.Advanced effects

8. Advanced effects

42. Lighting in a vertex, fragment shader

42.1 Lambert Lighting

  • Convert the modelled normal to world space
  • Get the dot product of the world normal and the light direction
  • CG function max(a,b) returns the larger of the two numbers, a and b.
  • Use nl to adjust the diffuse color 通过nl去控制光照漫反射强度,_LightColor0.rgb是本来应该的漫反射颜色,nl调控。

42.2 添加环境光

为什么地面现在如此黑暗呢?是因为我们没有接收环境光。

什么是环境光呢?我们用环境光来模拟间接光照。

什么是间接光照呢?也就是当光线在多个物体间反射然后进入摄像机。比如红地毯上放一个浅灰色沙发,那么沙发底部就会有红色。

  1. v2f中添加fixed3 ambient : COLOR1
  2. 在vert中赋值o.ambient = ShadeSH9(half4(worldNormal,1));
  3. 在frag中添加fixed3 lighting = i.diff*shadow + i.ambient
42.2.1 ShadeSH9是什么?

球谐函数,在BRDF中用来计算间接光的漫反射。

什么是球谐?球谐函数就是根据环境光漫反射生成的环境光贴图。

Unity计算的方式ShadeSH9(float4(i.normal,1)).

42.3 添加Shadow

  1. program multi_Compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight将shader编译成多个带或者不带阴影的变体,由于现在还不关心这些lightmap变体,所以跳过.
  2. #include "AutoLight.cginc"shadow 宏命令,所有的shadow相关的命令都在这个里面
  3. SHADOW_COORDS(1)  v2f结构体,把阴影放到texcoord1
  4. vert中TRANSFER_SHADOW(o)在顶点着色器中计算上一步中声明的阴影纹理坐标
  5. fixed shadow = SHADOW_ATTENUATION(i);在片元着色器中计算阴影值。
Shader "NiksShaders/Shader60Lit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //将shader编译成多个带或者不带阴影的变体
            //由于现在还不关心这些lightmap变体,所以跳过
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            //shadow 宏命令,所有的shadow相关的命令都在这个里面
            #include "AutoLight.cginc"
            
            

            struct v2f
            {
                float2 uv : TEXCOORD0;
                SHADOW_COORDS(1)  //把阴影放到texcoord1
                float4 pos : SV_POSITION;
                fixed3 diff : COLOR0;
                fixed3 ambient : COLOR1;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diff = nl * _LightColor0.rgb;//控制漫反射颜色
                o.ambient = ShadeSH9(half4(worldNormal,1));
                // compute shadows data
                TRANSFER_SHADOW(o)
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 col = tex2D(_MainTex, i.uv).rgb;
                // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
                fixed shadow = SHADOW_ATTENUATION(i);
                // darken light's illumination with shadow, keep ambient intact
                fixed3 lighting = i.diff * shadow + i.ambient;
                col *= lighting;

                return fixed4(col, 1);
            }
            ENDCG
        }

        // shadow casting support
        UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    }
}

43. A position location shader

思路就是通过脚本设置_Position,然后在Position的地方沿着xz方向画一个图形。

加入可控制的属性,最关键的属性_Position,在v2f中声明worldPos : TEXCOORD2存起来,在顶点着色器中利用mul将顶点变到世界空间o.worldPos = mul(unity_ObjectToWorld,v.vertex);,然后col+=_CircleColor* circle(i.worldPos.xz,_Position.xz,_Radius,0.1,0.001);画圆,lerp也可以。

Shader "NiksShaders/Shader61Lit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Position("Position",Vector) = (0,0,0,0)
        _Radius("Radius",Float) = 1
        _CircleColor("Circle Color",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            // compile shader into multiple variants, with and without shadows
            // (we don't care about any lightmaps yet, so skip these variants)
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
            // shadow helper functions and macros
            #include "AutoLight.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                SHADOW_COORDS(1) // put shadows data into TEXCOORD1
                fixed3 diff : COLOR0;
                fixed3 ambient : COLOR1;
                float4 pos : SV_POSITION;
                float4 worldPos : TEXCOORD2;
            };
            

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diff = nl * _LightColor0.rgb;//控制漫反射颜色
                o.ambient = ShadeSH9(half4(worldNormal,1));
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                // compute shadows data
                TRANSFER_SHADOW(o)
                return o;
            }

            sampler2D _MainTex;
            float4 _Position;
            float _Radius;
            fixed4 _CircleColor;
//画圆函数,pt是测试点
            float circle(float2 pt, float2 center, float radius, float line_width, float edge_thickness){
                float2 p = pt - center;
                float len = length(p);
                float half_line_width = line_width/2.0;
                float result = smoothstep(radius-half_line_width-edge_thickness, radius-half_line_width, len) - smoothstep(radius + half_line_width, radius + half_line_width + edge_thickness, len);

                return result;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 col = tex2D(_MainTex, i.uv).rgb;
                // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
                fixed shadow = SHADOW_ATTENUATION(i);
                // darken light's illumination with shadow, keep ambient intact
                fixed3 lighting = i.diff * shadow + i.ambient;
                col *= lighting;
                //col+=_CircleColor*  circle(i.worldPos.xz,_Position.xz,_Radius,0.1,0.001);
                fixed3 incircle = circle(i.worldPos.xz,_Position.xz,_Radius,0.1,0.001);//添加的
                fixed3 finalCol = lerp(col,_CircleColor,incircle);//添加的
                return fixed4(finalCol, 1);
            }
            ENDCG
        }

        // shadow casting support
        UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    }
}

44. Drawing a cross on the floor

自己的感觉还不错。

float cross(float2 pt,float2 center,float size,float line_width, float edge_thickness,float theta)
            {
                float c = cos(theta);
                float s = sin(theta);
                pt = mul(float2x2(c,s,-s,c),pt - center) + center;//将测试点逆时针旋转
                float2 p = pt - center;
                float half_line_width = line_width/2.0;
                float result = smoothstep(-half_line_width - edge_thickness,-half_line_width,p.x) - smoothstep(half_line_width - edge_thickness,half_line_width,p.x);
                result += smoothstep(-half_line_width - edge_thickness,-half_line_width,p.y) - smoothstep(half_line_width - edge_thickness,half_line_width,p.y);
                float finalResult =(1 - step(size * 0.5,length(p))) * result;
                return finalResult;
            }

老师的

float cross(float2 pt, float2 center, float radius, float line_width, float edge_thickness){
    float2 p = pt - center;
    float len = length(p);
    float half_line_width = line_width/2.0;
    float result = 0;
    if (len<radius){
        float horz = smoothstep(-half_line_width-edge_thickness, -half_line_width, p.y) - smoothstep( half_line_width, half_line_width + edge_thickness, p.y);
	float vert = smoothstep(-half_line_width-edge_thickness, -half_line_width, p.x) - smoothstep( half_line_width, half_line_width + edge_thickness, p.x);
	result = saturate(horz + vert);
    }
    return result;
}c

45. Tessellation 曲面细分

也不知道有什么用,先不写了~~

46. Using the stencil buffer

46.1 Redering a scene - frame buffer(帧缓冲)

帧缓存器,在CG/HLSL中,帧缓存器是用来当做颜色缓冲区,作为存储一个像素。流程是模版测试—深度测试—帧缓冲。一帧画面上的某一个像素基本上只有一个模版缓冲区,一个深度缓冲区,一个帧缓冲区。

利用32bit来存储一个RGBA,每个通道8bit。也就是利用frame buffer来存放像素绘制结果。

46.2 Rendering a scene - z buffer(深度缓冲)

z buffer用来记录frame buffer上,相机到渲染像素之间的距离,也就是z值。

if current fragment distance < current z buffer

then overrite frame buffer and update z buffer

如果目前的片元z值 < z buffer 中的z值,就更新zbuffer,然后覆盖帧缓冲中的像素。

(Unity基于左手系,摄像机z向正方向,z小代表距离近。OpenGL基于右手系,面向负轴)

46.3 Rendering a scene - stencil buffer

the same size as frame buffer。也就是说,模版测试和深度测试可以将多个物体渲染关联起来,模版测试可以通过模版来规定一个界限,通过就执行一个操作和不通过直接抛弃片元。

46.3.1 Unity提前定义的5个渲染队列 ——Queue

索引号小的先渲染,大的后渲染。

若果发现Unity下你的模板测试效果不对,但模板里Ref这些都没问题的话,那估计是RenderQueue设置的不合适。一般Mask的RenderQueue低于被Mask的对象。

Queue参数

名称队列索引号描述
Background1000最先渲染,背景物体
Geometry2000默认渲染,大多数物体,不透明物体
AlphaTest2450要进行透明度测试的物体。
Transparent3000这里后往前渲染,使用了透明度混合(例如关闭深度写入的shader)使用。
Overlay4000叠加效果,最后渲染的物体。

总的来说就是先渲染不透明的,再渲染透明的。

46.3.2 想让Quad不渲染自己的 ColorMask方法

ColorMask 0——写在CG前,通知unity不要渲染这个物体frame buffer中的像素,

ZWrite Off但是这时还不会影响scene的frame buffer,关闭深度写入是防止透明物体后面更远的物体被剔除。因为深度写入就是用来通过深度决定渲染哪个的。

Stencil{
	Ref 1 //参考值,如果写入模版缓冲区,写入值就是1
	Comp always//将参考值和当前缓冲区内容比较
	Pass replace//通过测试怎么做,这里就是替换模版缓冲区的值为1
}

Stencil 常用参数

参数说明
Ref参考值,参数允许赋值时候会赋值给当前像素,范围0~255
Comp比较方法,将当前的render pixel定义参考值和模版缓冲区的参考值进行比较,如果XX
Pass模版测试和深度测试都通过,处理
Fail都失败处理
ZFail模版通过,深度测试失败处理

Comp常用参数

Compmeans
GreaterRender pixels with Ref greater than buffer value.大于
GEqualRender pixels with Ref greater or equal to buffer value.大于等于
LessRender pixels with Ref less than buffer value.小于
LEqualRender pixels with Ref less than or equal to buffer value小于等于
EqualRender pixels with Ref equals to buffer value.
NotEqualRender pixels where Ref differs from buffer value.
AlwaysMake the stencil test always pass.让模版测试通过
NeverMake the stencil test always fail.让模版测试失败

Pass常用参数

PassMeans
KeepKeep buffer contents 默认是0,每帧刷新
ZeroWrite 0 to buffer
ReplaceWrite Ref value to buffer
IncrSatIncrement buffer value. Capped at 255.
DecrSatDecrement buffer value. Capped at 0.
InvertNegate the bits
IncrWrapIncrement buffer. 255 becomes 0.
DecrWrapDecrement buffer. 0 becomes 255.

两个shader如下:

不透明的物体:

Shader "NiksShaders/Shader63aLit"
{
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader {
      
      Tags { "Queue" = "Geometry" }
        stencil
        {
            Ref 1
            Comp NotEqual//不等于1的保持,等于1的舍弃
            Pass Keep
        }
    Cull Back//需要被穿透的前面通道

      CGPROGRAM
      
      #pragma surface surf Lambert
      
      struct Input {
          float2 uv_MainTex;
      };
      
      sampler2D _MainTex;
      
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      
      ENDCG
    Tags { "Queue" = "Geometry" }
        stencil
        {
            Ref 1
            Comp always//通过测试,不会被舍弃
            Pass Keep
        }
        Cull Front//后面通道

      CGPROGRAM
      
      #pragma surface surf Lambert
      
      struct Input {
          float2 uv_MainTex;
      };
      
      sampler2D _MainTex;
      
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      
      ENDCG

    } 

    Fallback "Diffuse"
}

遮罩物体:

Shader "NiksShaders/Shader63bLit"
{
    Properties {
    }

    SubShader {
      
      Tags { "Queue" = "Geometry-1" }
      ColorMask 0
      ZWrite Off
      stencil
      {
          Ref 1
          Comp always
          Pass replace
      }
      
      CGPROGRAM
      
      #pragma surface surf Lambert
      
      struct Input {
          float3 worldPos;//Because empty Input causes an error
      };
      
      
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = fixed4(1,1,1,1);
      }
      
      ENDCG
    } 

    Fallback "Diffuse"
}
43.3.3 Tips:遮罩要写入模版缓冲

我的理解:模版测试和深度测试通过复杂判断(通过测试可能改变模版缓冲区和深度缓冲区的值),来决定该片元最终是否被丢弃,不改变其他任何信息。

不通过直接丢弃,通过可以选择性改变缓冲区值,区别是深度测试关闭也可以选择写入。

When writing to the stencil buffer the shader must be earilier in the Queue then shaders reading from the buffer. 写入模版缓冲的shader必须队列比读取模版缓冲区的shader早渲染(Queue小)——这里具体体现在遮罩要先渲染,因为遮罩要写入模版缓冲

The Comp operation decides which pixels are able to write to the frame buffer

Comp决定哪些像素能够写到帧缓冲,也就是会被渲染

The Pass operation decides which pixels write to the stencil buffer.

Pass决定哪些像素写到模版缓冲区

ColorMask 0

47.Clipping the output

47.1 CG function clip

  • If the parameter passed to clip is less than 0
  • Output to the frame, z and stencil buffers will be ignored.

如果clip(a),a < 0 那么该像素就会被裁剪掉。

传统瓦片化的方式,利用frac函数,将位置放大倍数,取小数,这样就会导致每个位置都是(0,1):clip(frac(IN.worldPos.y*10) - (_SinTime.w + 1)/2);

定义出现时间:clip(frac(IN.worldPos.y * 10)-(1.0 - RevealTime)); 当RavelTime是1的时候就会显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值