shader篇-阴影

shader篇-阴影

标签(空格分隔): shader


简介

阴影的实现需要2个过程:
一、接受阴影的物体,需要在shader中采样阴影映射纹理,并把采样后结果与光照结果相乘获得阴影效果。
二、一个物体如果想投射阴影,就需要将该物体加入光照映射纹理的计算中,以便被采样时可以获取该物体相关信息。Unity Shader中该过程是又LightgMode 为ShadeowCaster的Pass实现。

投射阴影实现

Unity中,是否让物体投射或接受阴影,是由mesh renderer组件的Cast Shadows和Receive Shadows的设置来实现
不过即便没有设置,如果fallback能调用含LightMode 为ShadowCaster的Pass时,一样能投射阴影,比如说 Fallback “Specualr”,通过回调内置的Specular可以间接调用含有LightMode 为ShadowCaster的Pass。

接受阴影

编写接受阴影的shader时需要#include “AutoLight.cginc”,计算阴影的宏都是来自这个文件。
配置

Properties {
    _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    _Specular ("Specular", Color) = (1, 1, 1, 1)
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Tags { "RenderType"="Opaque" }

Pass {

    Tags { "LightMode"="ForwardBase" }

    CGPROGRAM


    #pragma multi_compile_fwdbase   

    #pragma vertex vert
    #pragma fragment frag


    #include "Lighting.cginc"
    #include "AutoLight.cginc"

    fixed4 _Diffuse;
    fixed4 _Specular;
    float _Gloss;

    struct a2v {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };

    struct v2f {
        float4 pos : SV_POSITION;
        float3 worldNormal : TEXCOORD0;
        float3 worldPos : TEXCOORD1;
        SHADOW_COORDS(2)
    };

注意:为保证内置宏运行正确,a2v结构体顶点坐标变量名必须
顶点着色器

v2f vert(a2v v) {
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);

    o.worldNormal = UnityObjectToWorldNormal(v.normal);

    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

    // 计算阴影值
    TRANSFER_SHADOW(o);

    return o;
}

之后,我们使用内置宏SHADOW_ATTENUATION在片元着色器中计算阴影值

Unity统一管理光照衰减和阴影

unity内置了一个宏计算光照衰减和阴影UNITY_LIGHT_ATTENUATION

v2f vert(a2v v) {
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);

    o.worldNormal = UnityObjectToWorldNormal(v.normal);

    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;


    TRANSFER_SHADOW(o);

    return o;
}

fixed4 frag(v2f i) : SV_Target {
    fixed3 worldNormal = normalize(i.worldNormal);
    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

    fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
    fixed3 halfDir = normalize(worldLightDir + viewDir);
    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);


    UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

    return fixed4((diffuse + specular) * atten, 1.0);
}

这种内置宏可以简化我们某个工作

透明度物体的阴影

对于大多数不透明物体,fallback设为VertexLit就可以得到正确的阴影,但不透明物体就要小心了。

配置

Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
}
SubShader {
Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}

Pass {
    Tags { "LightMode"="ForwardBase" }

    Cull Off

    CGPROGRAM

    #pragma multi_compile_fwdbase

    #pragma vertex vert
    #pragma fragment frag

    #include "Lighting.cginc"
    #include "AutoLight.cginc"

    fixed4 _Color;
    sampler2D _MainTex;
    float4 _MainTex_ST;
    fixed _Cutoff;

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

    struct v2f {
        float4 pos : SV_POSITION;
        float3 worldNormal : TEXCOORD0;
        float3 worldPos : TEXCOORD1;
        float2 uv : TEXCOORD2;
        SHADOW_COORDS(3)
    };

注意:SHADOW_COORDS的参数是3,意味着它将占有第四个插值寄存器TEXCOORD3

    FallBack "Transparent/Cutout/VertexLit"
}

还有fallback,这一次不能间接调用Vertexlit了,我们需要直接调用Transparent/Cutout/VertexLit,只有这样才能调用含透明度测试的shader

注意:Unity内置的半透明shader都不会添加阴影,因为半透明物体关闭了深度写入,必须处理好渲染顺序,否则阴影处理会非常复杂

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值