首页 > 其他分享 >Unity Shader 实现 Lambert Phong BlinnPhong

Unity Shader 实现 Lambert Phong BlinnPhong

时间:2023-03-24 15:11:23浏览次数:45  
标签:normalize BlinnPhong Lambert Shader rgb Phong float4 MainTex float3

1.Lambert模型

        • 基于Lambert余弦定理构造出的模型,只计算了漫反射部分

代码部分解析

    • 可以看到Lambert模型中只返回了漫反射(diffuse)
    •  
    • 再看一下diffuse是什么组成的
    •  
    • 第一项为主光源的入射光颜色
    • 第二项为漫反射材质的颜色
    • 第三项 法线方向和光照方向的点积(cosθ=n.l)
    •  

       

 2.Phong模型

代码部分解析

 

  • 计算反射光方向,用着色器自带的reflect函数计算。注意:这里的光照方向是指往外的方向,所以要取反。
  • 高光部分需要的反射方向(r)和观察方向(v)的余弦值(这里cos等于两者点积)
    • 观察/视线方向如下:通过相机位置减去顶点像素点位置获得(世界坐标下做的运算)
    • 漫反射部分已经在上边Lambert模型中计算完成 

    • 计算高光部分=入射光的颜色 * 高光材质颜色 * (v·r)gloss

    • 环境光量(Unity的一个宏)* 漫反射材质
    •  

       

3.Blinn-Phone模型

 

  • 对Phong模型进行了改进,不用反射向量来计算高光
  • 关键:引入了半程向量h
  • 将高光部分的计算改为了法线(n)和半程向量(h)的点积
  • 指数依然是gloss(GAMES101中的p指数)

    代码部分解析

    • 半程向量的计算:光照方向和观察方向相加,再归一化
    • NdotH:就是半程向量和法线向量的夹角的余弦。得到高光方向

 

4.Phong模型和Blinn-Phong模型的区别

  • 总体来说就是Blinn-Phong计算更简单
  • 特殊情况:Phong模型中,材质反光度很低时,一部分高光的反射向量和观察方向夹角超过90°,就会产生高光缺失、断层的效果。 

5.Gourand模型

  • 逐顶点计算
  • 镜面高光效果差
  • 对应的着色频率:Gouraud shading(逐顶点)算出顶点法线,然后插值

 

  • 是在顶点着色器中计算的。(因为是逐顶点的)
  • 顶点颜色使用的是Phong模型的结果
  • 高光部分用的是观察方向和反射方向的余弦值
  • 计算完之后将它作为顶点所携带的颜色
  • 在片元/像素着色器中,直接将顶点携带的颜色赋值给了返回的颜色

具体实现代码

Shader "Custom/BaseLighting"
{
    Properties
    {
        
        _Diffuse ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Specular("Specular",Color) = (1,1,1,1)
        _Glossiness ("Smoothness", Range(1.0,255)) = 20
        _NormalMap("NormalMap",2D) = "white"{}
        _LocalNormalSild("LocalNormal",Range(0,1)) = 0
        _CubeMap("Env Map",CUBE)= ""{} 
        _MipScale("MipScale",Range(0,10)) =0
        _EnvScale("EnvScale",Range(0,1)) = 1
        [Toggle] _Phong("Phone", Int) = 0
        [Toggle] _BlinnPhong("Binn Phone", Int) = 0
        [Toggle] _IBL("IBL", Int) = 0

        //_Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags{"RenderType" = "Opaque"}
        LOD 100

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

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

        
        
        float4 _Diffuse;
        float4 _Specular;
        float _Glossiness;
        float _LocalNormalSild;
        sampler2D _MainTex ; float4 _MainTex_ST;
        uniform sampler2D _NormalMap; uniform float4 _NormalMap_ST;
        samplerCUBE _CubeMap;
        float _MipScale;
        float _EnvScale;
        bool _Phong;
        bool _BlinnPhong;
        bool _IBL;
        


        struct appdata{
            float4 vertex :POSITION;
            float2 uv:TEXCOORD0;
            float3 normal :NORMAL;
            float4 tangent :TANGENT;

        };
        struct v2f {
            float4 Pos :SV_POSITION;
            float3 worldNormal: NORMAL;
            float2 uv :TEXCOORD0;
            
            float3 tangentDir :TEXCOORD1;
            float3 bitangentDir :TEXCOORD2;
            float3 worldPos :TEXCOORD3;
            float3 normalDir: TEXCOORD4;
            

        };
        v2f vert(appdata v){
            v2f o;
            o.Pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.uv;
            o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
            o.worldNormal = UnityObjectToWorldNormal(v.normal);
            //计算切线和副切线
            o.normalDir = UnityObjectToWorldNormal(v.normal);
            o.tangentDir = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz);
            o.bitangentDir = normalize( cross(o.normalDir,o.tangentDir).xyz*v.tangent.w);//乘tangent.w来确定切线方向
            
            return o;
        
        }

        fixed4 frag(v2f i):SV_Target{
            float4 MainTex = tex2D(_MainTex,TRANSFORM_TEX(i.uv,_MainTex));//TRANSFORM_TEX用于控制纹理贴图的缩放和位移
            float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz *_Diffuse*MainTex.rgb;
            //光方向
            float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
            float3 worldNormal = normalize(i.worldNormal);
            i.worldNormal = worldNormal;
            float3 LightColor = _LightColor0.rbg;
            //TBN
            float3x3 tangentTransform = float3x3(i.tangentDir,i.bitangentDir,i.normalDir);

            //获取映射过去的法线数据(法线自身数据 需要通过TNB矩阵变换到世界空间)
            float3 normalLocal = UnpackNormal(tex2D(_NormalMap,TRANSFORM_TEX(i.uv,_NormalMap)));
            float3 normalWorld = normalize(mul(normalLocal,tangentTransform));

            float3 finiNormal = lerp(worldNormal,normalWorld,_LocalNormalSild);
            float NotL = max(0.0,dot(finiNormal,worldLight));
            //lambert计算法线后背面一般为黑色 这里使用环境光和光照做插值
            float3 diffuse = lerp(ambient.rgb*_Diffuse.rgb*MainTex.rgb,_LightColor0.rgb*_Diffuse.rgb*MainTex.rgb,NotL);
            //Lambert 结果
            
            float3 color = LightColor*MainTex.rgb*NotL;
            
            
            //Phong (比Lambert多了环境光和高光反射)
            //-worldLight表示入射光方向
            float3 reflectDir = normalize(reflect(-worldLight,finiNormal));
            float3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
            float VdotR = max(0.0,dot(reflectDir,viewDir));
            float3 specular = _LightColor0.rgb*_Specular.rgb*pow(VdotR,_Glossiness);
            //phone结果
            
             if(_Phong){
                color = diffuse + ambient+ specular;
             }
            
            //Blinn-phong(比Phong在计算高光时使用半程向量 计算更快 光照更真实 )
            float3 halfDir = normalize(worldLight+viewDir);
            //
            float NdotH = saturate(dot(halfDir,finiNormal));//将数值规范在0-1
            specular = _LightColor0.rgb*_Specular.rgb*pow(NdotH,_Glossiness);
            //Blin-phong结果
            if(_BlinnPhong){
                color = diffuse + ambient+ specular;
            }
            //环境贴图
            float3 worldRef = normalize(reflect(-viewDir,finiNormal));
            float4 reflcol = texCUBElod(_CubeMap,float4(worldRef.rgb,(255-_Glossiness)*8/(255)))*_EnvScale;

            specular = _LightColor0.rgb*_Specular.rgb*pow(NdotH,_Glossiness);
            specular = lerp(diffuse*specular,specular,_Glossiness/255);
            reflcol.rgb = lerp(reflcol*diffuse.rgb,reflcol,_Glossiness/255);
            if(_IBL){
                color = diffuse + reflcol.rgb + specular;
            }
            


            //float3 worldNormal = normalize(i.worldNormal);
            //float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//世界光方向
            //float3 lightColor = _LightColor0.rgb;
            //float3 ambient  = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse;
            //float3 directLight = max(0,dot(worldNormal,worldLight))*lightColor;
            //float3 MainTex = tex2D(_MainTex,i.uv).rgb;
            //float3 diffuseColor = MainTex *(directLight+ambient);
            
            return float4(color,1);

        }
        ENDCG
        }
        
    }
    FallBack "Diffuse"
}
View Code

效果对比

Lambert

 

 Phong

 

 BlinnPhong

 

 

标签:normalize,BlinnPhong,Lambert,Shader,rgb,Phong,float4,MainTex,float3
From: https://www.cnblogs.com/kayiko/p/17251831.html

相关文章

  • 如果设备不支持vulkan,就用swiftshader,否则就加载系统的vulkan的正确姿势(让程序能够智
    一些老的显卡设备没有更新驱动,甚至根本就不支持Vulkan的显卡,遇到静态链接的vulkan-1.lib文件,启动exe就会崩溃。 你以为从别的机器拷贝这个vulkan-1.dll就可以了吗?太傻......
  • Three.js自定义shader实现离相机越近越透明效果
    constcustomShader=newTHREE.ShaderMaterial({uniforms:{},defines:{nearDis:0.3},transparent:true,side:THREE.DoubleSide,vertexShader:......
  • Unity Shader之皮肤渲染之用颜色实现sss效果
    参考文章:https://zhuanlan.zhihu.com/p/571468873shader面板参数如下:漫反射部分使用了ramp贴图,和3层上色,来模拟SSS的效果。Ramp贴图从左到右,从白色到暗色,u坐标使用NoL......
  • QOpenGLShader Class
    DetailedDescription此类支持使用OpenGL着色语言(GLSL)和OpenGL/ES着色语言(GLSL/ES)编写的着色器。QOpenGLShader和QOpenGLShaderProgram使程序员免于编译和......
  • Shader 入门:GLSL ES(数据类型)
    在上一篇文章中我们初步了解了GLSLES的基本语法,那么本篇文章就和大家一起学习GLSLES的数据类型。Let’sgo!!!上一篇:《Shader入门:GLSLES(简介和基本语法)》在本系......
  • Cesium渲染模块之Shader - 当时明月在曾照彩云归
    1.引言Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业......
  • shadertoy学习 第3课
    注意下面的segment函数,计算了uv点到直线的距离,小于width就是白色//封装绘制网格函数vec3grid(vec2uv){vec2cell=fract(uv);//取小数部分vec3col=vec3(0.);//网格绘制......
  • GLSL shader学习系列2-画个圆
    这是GLSLshader学习系列的第二篇文章,本文中我们将会学习如何使用shader绘制一个圆形。引子上一篇文章的内容比较简单易懂,由于gl_FragCoord能够表示每个像素点在画布中的......
  • 三大 Shader 编程语言(CG/HLSL/GLSL)
    什么是ShaderLanguageShaderLanguage的发展方向是设计出在便携性方面可以和C++、Java等相比的高级语言,“赋予程序员灵活而方便的编程方式”,并“尽可能的控制渲染过程”......
  • shadertoy学习 第2课
    坐标系统绘制与fwidth函数voidmainImage(outvec4fragColor,invec2fragCoord){//Normalizedpixelcoordinates(from-1to1)vec2uv=(2.0*fragCoord-iResolut......