【Aladdin Unity3D Shader编程】之三 光照模型(二)

高光反射模型

Specular=直射光*pow(cosθ,高光的参数) θ:是反射光和视野方向的夹角

编写高光反射Shader

Shader "AladdinShader/07 Specular Vertex Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
    }
    SubShader {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;

        struct a2v
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            fixed3 color:COLOR;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            fixed3 adbient = UNITY_LIGHTMODEL_AMBIENT.rgb;
            fixed3 normalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
            fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说 光的位置就是光的方向 因为是平行光
            fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir), 0) * _Diffuse.rgb;//取得漫反射的颜色

            //反射光方向
            fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
            //视野方向
            //相机是在世界空间下
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex,unity_WorldToObject).xyz);

            //高光反射
            fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir,viewDir),0),10);
            f.color = diffuse + adbient + specular;
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            return fixed4(f.color, 1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果图:

添加_Gloss和SpecularColor控制高光大小和高光颜色

添加一个Range属性,将X次幂的值改成Range属性,做如下修改:

_Gloss("Gloss",Range(8,200)) = 10
//高光反射
fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir,viewDir),0),_Gloss);

就会看到高光光圈随着_Gloss改变而改变

想要改变高光部分的颜色控制,我们添加一个颜色变量然后将高光颜色与我们设置的颜色属性进行融合计算就会得到新的高光部分的颜色

Shader "AladdinShader/07 Specular Vertex Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8,200)) = 10
    }
    SubShader {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;
        fixed4 _Specular;
        half _Gloss;

        struct a2v
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            fixed3 color:COLOR;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            fixed3 adbient = UNITY_LIGHTMODEL_AMBIENT.rgb;
            fixed3 normalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
            fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说 光的位置就是光的方向 因为是平行光
            fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir), 0) * _Diffuse.rgb;//取得漫反射的颜色

            //反射光方向
            fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
            //视野方向
            //相机是在世界空间下
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex,unity_WorldToObject).xyz);

            //高光反射
            fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir,viewDir),0),_Gloss) * _Specular.rgb;
            f.color = diffuse + adbient + specular;
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            return fixed4(f.color, 1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果图:

实现逐像素的高光反射

Shader "AladdinShader/08 Specular Fragment Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8,200)) = 10
    }
    SubShader {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;
        fixed4 _Specular;
        half _Gloss;

        struct a2v
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            float3 worldNormal:TEXCOORD0;//世界空间下的法线方向
            float3 worldVertext:TEXCOORD1;//时间空间下的顶点坐标
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            f.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
            f.worldVertext = mul(v.vertex , unity_WorldToObject).xyz;
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            fixed3 adbient = UNITY_LIGHTMODEL_AMBIENT.rgb;
            fixed3 normalDir = normalize(f.worldNormal);
            fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说 光的位置就是光的方向 因为是平行光
            fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir), 0) * _Diffuse.rgb;//取得漫反射的颜色

            //反射光方向
            fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
            //视野方向
            //相机是在世界空间下
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldVertext);

            //高光反射
            fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir,viewDir),0),_Gloss) * _Specular.rgb;
            fixed3 tempColor = diffuse + adbient + specular;
            return fixed4(tempColor, 1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果图:

会比逐顶点高光效果更好一些,背光面可以考虑之前说的半兰伯特来处理,这样就不会显示全黑效果不好的情况。

使用Blinn-Phong光照模型

Specular=直射光颜色*pow(max(cosθ,0),10) θ:是发现和x的夹角 x是平行光和视野方向的平分线

Shader "AladdinShader/09 Specular Fragment Blinn Phong Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8,200)) = 10
    }
    SubShader {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;
        fixed4 _Specular;
        half _Gloss;

        struct a2v
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            float3 worldNormal:TEXCOORD0;//世界空间下的法线方向
            float3 worldVertext:TEXCOORD1;//时间空间下的顶点坐标
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            f.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
            f.worldVertext = mul(v.vertex , unity_WorldToObject).xyz;
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            fixed3 adbient = UNITY_LIGHTMODEL_AMBIENT.rgb;
            fixed3 normalDir = normalize(f.worldNormal);
            fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说 光的位置就是光的方向 因为是平行光
            fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir), 0) * _Diffuse.rgb;//取得漫反射的颜色

            //反射光方向
            //fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
            //视野方向
            //相机是在世界空间下
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldVertext);

            //平分线
            fixed3 halfDir = normalize(viewDir + lightDir);  //视野方向和光照方向的平分线

            //高光反射
            fixed3 specular = _LightColor0.rgb * pow(max(dot(normalDir,halfDir),0),_Gloss) * _Specular.rgb;
            fixed3 tempColor = diffuse + adbient + specular;
            return fixed4(tempColor, 1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}


会发现高光部分会显得更大了,这也是平时用的比较多的高光反射模型。

如何使用Unity的内置函数

UnityCG.cginc中一些常用的函数

//摄像机方向(视野方向)
* float3 WorldSpaceViewDir(float4 v) 根据模型空间中的顶点坐标得到(世界空间)从这个点到摄像机的观察方向
* float3 UnityWorldSpaceViewDir(float4 v) 世界空间中的顶点坐标=>世界空间从这个点到摄像机的观察方向
* float3 ObjSpaceViewDir(float4 v) 模型空间中的顶点坐标=>模型空间从这个点到摄像机的观察方向

//光源方向
* float3 WorldSpaceLightDir(float4 v) 模型空间中的顶点坐标=>世界空间中从这个点到光源的方向
* float3 UnityWorldSpaceLightDir(float4 v) 世界空间中的顶点坐标=>世界空间中从这个点到光源的方向
* float3 ObjSpaceLightDir(float4 v) 模型空间中的顶点坐标=>模型空间中从这个点到光源的方向

//方向转换
float3 UnityObjectToWorldNormal(float3 norm) 把法线方向 模型空间=>世界空间
float3 UnityObjectToWorldDir(float3 dir) 把方向 模型空间=>世界空间
float3 UnityWorldToObjectDir(float3 dir) 把方向 世界空间=>模型空间

上面BP模型修改:

Shader "AladdinShader/09 Specular Fragment Blinn Phong Shader"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1) //添加自身的颜色
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8,200)) = 10
    }
    SubShader {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

        CGPROGRAM
#include "Lighting.cginc" //引用一些写好的程序块 会包含一些获取光照的信息
//_LightColor0 取得第一个直射光的颜色
//_WorldSpaceLightPos0 

#pragma vertex vert
#pragma fragment frag 

        fixed4 _Diffuse;
        fixed4 _Specular;
        half _Gloss;

        struct a2v
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL; //模型空间下法线
        };

        struct v2f
        {
            float4 position:SV_POSITION;
            float3 worldNormal:TEXCOORD0;//世界空间下的法线方向
            float4 worldVertext:TEXCOORD1;//时间空间下的顶点坐标
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            //法线从模型空间转成世界空间下
            // f.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
            f.worldNormal = UnityObjectToWorldNormal(v.normal); //使用内置方法转换
            f.worldVertext = mul(v.vertex , unity_WorldToObject);
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            fixed3 adbient = UNITY_LIGHTMODEL_AMBIENT.rgb;
            fixed3 normalDir = normalize(f.worldNormal);

            //光源方向
            // fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//对于每个顶点来说 光的位置就是光的方向 因为是平行光
            fixed3 lightDir = normalize(WorldSpaceLightDir(f.worldVertext).xyz); //模型空间中的顶点坐标=>世界空间中从这个点到光源的方向

            fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir,lightDir), 0) * _Diffuse.rgb;//取得漫反射的颜色

            //反射光方向
            //fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
            //视野方向
            //相机是在世界空间下
            // fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldVertext);
            fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldVertext));
            //平分线
            fixed3 halfDir = normalize(viewDir + lightDir);  //视野方向和光照方向的平分线

            //高光反射
            fixed3 specular = _LightColor0.rgb * pow(max(dot(normalDir,halfDir),0),_Gloss) * _Specular.rgb;
            fixed3 tempColor = diffuse + adbient + specular;
            return fixed4(tempColor, 1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}


运行效果还是跟上面效果一样,BP模型是常用的模型,会发现背光面并没有一些奇怪的光斑,更符合生活常理。

漫反射和高光反射统一实现

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

Shader "AladdinShader/10 Diffuse Specular Shader"
{
    Properties
    {
        _Diffuse("Diffuse Color", Color)=(1,1,1,1)
        _Specular("Specular Color", Color)=(1,1,1,1)
        _Gloss("Gloss",Range(10,200))=20
    }
    SubShader {
        Pass{
            //只有正确定义Tags 才能获取跟光相关的属性
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag 

            fixed4 _Diffuse;
            fixed4 _Specular;
            half _Gloss;
            //顶点函数参数
            struct a2v{
                float4 vertex:POSITION; //顶点位置
                float3 normal:NORMAL; //模型空间下的法线
            };

            struct v2f{
                float4 svPos:SV_POSITION;
                fixed3 worldNormal:TEXCOORD0;//世界空间下的法线
                float4 worldVertex:TEXCOORD1;
            };

            v2f vert(a2v v)
            {
                v2f f;
                f.svPos = mul(UNITY_MATRIX_MVP,v.vertex); //模型空间位置到剪裁空间的顶点位置的转换
                f.worldNormal = UnityObjectToWorldNormal(v.normal); //模型空间的法线转成时间空间下的法线
                f.worldVertex = mul(v.vertex,unity_WorldToObject);
                return f;
            } 

            //片元函数返回颜色
            fixed4 frag(v2f f):SV_Target{
                //漫反射
                //漫反射颜色 先不管透明度
                //_LightColor0 平行光的颜色 cos夹角 光的方向和视野的夹角
                fixed3 normalDir = normalize(f.worldNormal);
                //光的方向
                fixed3 lightDir = normalize(WorldSpaceLightDir(f.worldVertex));
                //漫反射的颜色
                fixed3 diffuse = _LightColor0.rgb *_Diffuse.rgb * max(dot(normalDir, lightDir),0);

                //相机方向
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldVertex));

                //光和相机方向的平分线
                fixed3 halfDir = normalize(lightDir + viewDir);
                //高光反射
                fixed3  specular = _LightColor0.rgb * _Specular.rgb * pow(max(dot(normalDir,halfDir),0),_Gloss);
                //环境光
                fixed3 tempColor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rgb;
                return fixed4(tempColor,1);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

以上这一段一定要熟练敲熟并且弄懂。效果图也跟上面效果是差不多的。

Shader学习交流群:
316977780

时间: 2024-12-29 22:34:24

【Aladdin Unity3D Shader编程】之三 光照模型(二)的相关文章

【Aladdin Unity3D Shader编程】之二 光照模型(一)

光照模型 光照模型就是一个公式,使用这个公式来计算在某个点的光照效果. 在标准光照模型里面,我们把进入摄像机的光分为下面四个部分: * 自发光 类似生活中的萤火虫等自己能够发光 * 高光反射 类似生活中的镜子,近似认为百分百反射出去 * 漫反射 类似生活中的光照射到墙壁上.桌子上的反光不会百分百反射出去,各个方向都会反射. * 环境光 类似生活中的光照照射在某个物体上,物体漫反射然后反射到其他物体上这些过程的光是环境光. 以上都是模拟生活中的光照效果,并不等同于实际生活中的光照计算,实际生活中的

【Aladdin Unity3D Shader编程】之一 基本入门

OpenGL.DirectX以及GLSL.HLSL.CG OpenGL和DirectX是图像应用编程接口,用于渲染二维或者三维图形. GLSL着色语言是用来在OpenGL中着色编程的语言,有点在于跨平台性,可以再Windows.Linux.Mac甚至移动平台上工作. HLSL是微软控制着色的编译,几乎只支持微软自己的产品,如Windows,XBox等,其他平台没有可编译HLSL的编译器. CG是有英伟达公司出的真正意义上的跨平台着色器语言. GPU渲染管线概述 1.顶点着色器 顶点着色器是流水线

猫都能学会的Unity3D Shader入门指南(二)

关于本系列 这是Unity3D Shader入门指南系列的第二篇,本系列面向的对象是新接触Shader开发的Unity3D使用者,因为我本身自己也是Shader初学者,因此可能会存在错误或者疏漏,如果您在Shader开发上有所心得,很欢迎并恳请您指出文中纰漏,我会尽快改正.在之前的开篇中介绍了一些Shader的基本知识,包括ShaderLab的基本结构和语法,以及简单逐句地讲解了一个基本的shader.在具有这些基础知识后,阅读简单的shader应该不会有太大问题,在继续教程之前简单阅读一下Un

猫都能学会的Unity3D Shader入门指南(一)

动机 自己使用Unity3D也有一段时间了,但是很多时候是流于表面,更多地是把这个引擎简单地用作脚本控制,而对更深入一些的层次几乎没有了解.虽然说Unity引擎设计的初衷就是创建简单的不需要开发者操心的谁都能用的3D引擎,但是只是肤浅的使用,可能是无法达到随心所欲的境地的,因此,这种状况必须改变!从哪里开始呢,貌似有句话叫做会写Shader的都是高手,于是,想大概看看从Shader开始能不能使自己到达的层次能再深入一些吧,再于是,有了这个系列(希望我能坚持写完它,虽然应该会拖个半年左右). Un

Unity3D shader简介

Unity3D shader简介 可以肯定的说Unity3D使得很多开发者开发游戏更容易.毫无疑问,shader(着色器)编码,仍有很长的路要走.shader是一个专门运行在GPU的程序,经常被神秘包围,它最终绘制3D模型的三角形.如果你想给游戏一个特殊的显示,学习如何编写shader是必要的.Unity3D使用shader做后期处理,对2D游戏也是必不可少的.这个系列的文章将逐步介绍shader编程,并面向几乎没有任何shader知识的开发者. 简介 下图大致表示了在Unity3D渲染流程中发

iOS多线程编程之三——GCD的应用

iOS多线程编程之三--GCD的应用 一.引言 在软件开发中使用多线程可以大大的提升用户体验度,增加工作效率.iOS系统中提供了多种分线程编程的方法,在前两篇博客都有提及: NSThread类进行多线程编程:http://my.oschina.net/u/2340880/blog/416524. NSOperation进行多线程操作编程:http://my.oschina.net/u/2340880/blog/416782. 上两个进行多线程编程的机制都是封装于Object-C的类与方法.这篇博

iOS网络编程之三——NSURLConnection的简单使用

iOS网络编程之三--NSURLConnection的简单使用 一.引言     在iOS7后,NSURLSession基本代替了NSURLConnection进行网络开发,在iOS9后,NSURLConnection相关方法被完全的弃用,iOS系统有向下兼容的特性,尽管NSURLConnection已经被弃用,但在开发中,其方法依然可以被使用,并且如果需要兼容到很低版本的iOS系统,有时就必须使用NSURLConnection类了. 二.使用NSURLConnection进行同步请求     

.NET 4 并行(多核)编程系列之二 从Task开始

原文:.NET 4 并行(多核)编程系列之二 从Task开始 .NET 4 并行(多核)编程系列之二 从Task开始          前言:我们一步步的从简单的开始讲述,还是沿用我一直的方式:慢慢演化,步步为营.     本篇文章的议题如下:    1.Task基础介绍    2.Task的创建     3.获取Task的执行结果 4. 补充细节   系列文章链接: .NET 4 并行(多核)编程系列之一入门介绍 .NET 4 并行(多核)编程系列之二 从Task开始  .NET 4 并行(多

实战JSP进阶编程之三:在Tomcat下配置Hibernate的开发环境

这是实战JSP进阶编程之三. 今天花了几个小时,终于将机房里面的Tomcat+Hibernate的开发.学习环境配置好了. 应用场景:Tomcat 5.5, Hibernate 2.1.7, Mysql 3.23.43, Mysql Driver:3.0.14, JDK: 1.4.2 OS: TurboLinux Server 8.0 用户环境:普通学生帐户--j2ee,位置: /home/j2ee/public_html 为了方便初学者,本教程特意作了简化处理. 1.将hibernate2.j