shader之半兰伯特漫反射

看很多人实现shader都用插件shader force,那我还学shader干X!!!???

好了,废话不多说,学习shader去。。。。


漫反射在shader里算是最基础的知识了。入手shader,你必须可以信手拈来一个漫反射吧??什么叫信手拈来,就是不查阅资料的前提下吧

那我就信手拈来一个半兰伯特漫反射效果吧(瞄了一眼资料,好像没人看见)

正常的漫反射公式(Lambert)是这样的:

C(diffuse) = C(light) * M(diffuse)max(0,n*I)

C(light):入射光的颜色和强度

M(diffuse):材质的漫反射系数

n:世界坐标系下的表面法线

I:世界坐标系下的光源方向(反射点指向光源的矢量)

max函数:,将结果截取到0,防止法线与光源方向的点乘为负值,被背后的光源照亮的错误效果

 

而笔者会比较喜欢一个毫无依据的半兰伯特公式:

C(diffuse) = (C(light) * M(diffuse))(0.5(n*l)+0.5)

这个公式实现的漫反射效果看起来更真实一些(据说是让亮度更亮一点,特别是比较暗的地方)


好了,又要放代码了:

笔者是在顶点着色器中计算空间法线和空间灯光矢量,为的是让效果更佳精确点

但是同样笔者也有个疑问:我尝试在顶点着色器里面对贴图纹理进行采样,但是报错了,不知道为什么不能这样做?

Shader "CharmingShader/Wangzhe/Diffuse"
{
    Properties
    {
        _MainTex("Main Tex",2D) = "white"{}
        _DiffuseColor("Diffuse Color",Color) = (1,1,1,1)
    }
        SubShader
        {
            Tags{"Queque" = "Opaque"}
            Pass
            {
                Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
                #include "Lighting.cginc"

                sampler2D _MainTex;
                float4 _MainTex_ST;
                float4 _DiffuseColor;

                struct a2v
                {
                    float4 vertex : POSITION;
                    float4 texcoord : TEXCOORD0;
                    float3 normal :NORMAL;
                };
                struct v2f
                {
                    float4 pos : SV_POSITION;
                    //float3 color : COLOR;
                    float halfLambert : TEXCOORD0;
                    float2 uv : TEXCOORD1;
                };

                v2f vert(a2v v)
                {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    float3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
                    float3 worldPos = mul(unity_ObjectToWorld, o.pos);
                    float3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
                    o.halfLambert = dot(worldNormal, worldLightDir)*0.5 + 0.5;
                    
                    
                    return o;
                }

                float4 frag(v2f f) : SV_Target
                {
                    float3 col = tex2D(_MainTex, f.uv).rgb;
                    float3 diffuse = col * _DiffuseColor.rgb;
                    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * col;
                    float3 color = ambient + _LightColor0.rgb * diffuse * f.halfLambert;
                    return float4(color,1);
                }

                ENDCG
        }
    }
}

这里要注意一点,不管是在pass前面的tags还是pass里面的tags都是尤为重要的。

Tags标签是一个键值对,它们告诉Unity的渲染引擎如何及何时去渲染这个对象

这里用到的Tags{"LightMode" = "FarwordBase"},在官方文档是这样解释的:

Details

Tags are basically key-value pairs. Inside a Pass tags are used to control which role this pass has in the lighting pipeline (ambient, vertex lit, pixel lit etc.) and some other options. Note that the following tags recognized by Unity must be inside Pass section and not inside SubShader!

LightMode tag

LightMode tag defines Pass’ role in the lighting pipeline. See render pipeline for details. These tags are rarely used manually; most often shaders that need to interact with lighting are written as Surface Shaders and then all those details are taken care of.

Possible values for LightMode tag are:

  • Always: Always rendered; no lighting is applied.
  • ForwardBase: Used in Forward rendering, ambient, main directional light, vertex/SH lights and lightmaps are applied.
  • ForwardAdd: Used in Forward rendering; additive per-pixel lights are applied, one pass per light.
  • Deferred: Used in Deferred Shading; renders g-buffer.
  • ShadowCaster: Renders object depth into the shadowmap or a depth texture.
  • MotionVectors: Used to calculate per-object motion vectors.
  • PrepassBase: Used in legacy Deferred Lighting, renders normals and specular exponent.
  • PrepassFinal: Used in legacy Deferred Lighting, renders final color by combining textures, lighting and emission.
  • Vertex: Used in legacy Vertex Lit rendering when object is not lightmapped; all vertex lights are applied.
  • VertexLMRGBM: Used in legacy Vertex Lit rendering when object is lightmapped; on platforms where lightmap is RGBM encoded (PC & console).
  • VertexLM: Used in legacy Vertex Lit rendering when object is lightmapped; on platforms where lightmap is double-LDR encoded (mobile platforms).

意思是:LightMode标签是在Pass里面,管理处在光照管道中的pass和其它选项。注意LightMode标签必须处在Pass里面而不是SubShader里面。

由于时间的关系,我就只翻译一下ForwardBase:ForwardBase用在Forward渲染中,渲染时可以应用到环境光、主平行光、顶点光和球谐光照以及光照贴图。

之前一直忽视了这个标签的作用,导致如何调效果都灰常暗。

下面放两张对比图吧:

普通贴图效果------------------------------------------------->半兰伯特漫反射效果

 

虽然半兰伯特效果看起来更自然点,但是相对的亮度也比较暗了,反而没有图一看起来惊艳,而仅仅是更写实了点。

(有一位小萌同事也提出了这个问题:喵~~脸这么黑,哪里好看了!

我当时心里嘀咕到:想要怎样就怎样,哪有这么好的事情哦?)

 

 


 

(后来一次机缘巧合,得到了一块秘籍残片)咳咳,哪有这么神话。也就是我想装逼翻译文章遇到很多不会的名词,比如说SH Lights,

然后我就贴到Unity Shader群里问了问, 当时有个头衔为话痨id为落俗的小伙伴很热情的为我解答,并且贴出一段能让这个漫反射更好

看的代码。

SH Lights就是球谐光照,利用球谐光照技术可以实时重现面积光源下3D模型的全局光照效果(好吧,我也不懂,我就记住一个球)

代码是这样的(据说要有天空盒才会有效果)

在v2f结构体里定义sh

在顶点着色器里面计算sh,注意这里要对v2f o进行一次初始化

最后在片源着色器输出颜色里加上对sh的处理

Ctrl+S跳转界面,噔噔噔噔,有没被惊艳到= =


 这里贴上完整的代码:

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "CharmingShader/Wangzhe/Diffuse"
{
    Properties
    {
        _MainTex("Main Tex",2D) = "white"{}
        _DiffuseColor("Diffuse Color",Color) = (1,1,1,1)
    }
        SubShader
        {
            Tags{"Queque" = "Opaque"}
            Pass
            {
                Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
                #include "Lighting.cginc"

                sampler2D _MainTex;
                float4 _MainTex_ST;
                float4 _DiffuseColor;

                struct a2v
                {
                    float4 vertex : POSITION;
                    float4 texcoord : TEXCOORD0;
                    float3 normal :NORMAL;
                };
                struct v2f
                {
                    float4 pos : SV_POSITION;
                    //float3 color : COLOR;
                    float halfLambert : TEXCOORD0;
                    float2 uv : TEXCOORD1;
                    #if UNITY_SHOULD_SAMPLE_SH
                    float3 sh : TEXCOORD2;
                    #endif
                };

                v2f vert(a2v v)
                {
                    v2f o = (v2f)0;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    float3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
                    float3 worldPos = mul(unity_ObjectToWorld, o.pos);
                    float3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
                    #ifndef LIGHTMAP_ON
                    #if UNITY_SHOULD_SAMPLE_SH
                    o.sh = ShadeSHPerVertex(worldNormal, o.sh);
                    #endif
                    #endif
                    o.halfLambert = dot(worldNormal, worldLightDir)*0.5 + 0.5;
                    
                    
                    return o;
                }

                float4 frag(v2f f) : SV_Target
                {
                    float3 col = tex2D(_MainTex, f.uv).rgb;
                    float3 diffuse = col * _DiffuseColor.rgb;
                    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * col;
                    float3 color = ambient + _LightColor0.rgb * diffuse * f.halfLambert + diffuse * f.sh;
                    return float4(color,1);
                }

                ENDCG
        }
    }
}
View Code

 

posted @ 2017-08-16 16:07  小洋大海  阅读(3676)  评论(6编辑  收藏  举报