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

光照模型

光照模型就是一个公式,使用这个公式来计算在某个点的光照效果。
在标准光照模型里面,我们把进入摄像机的光分为下面四个部分:
* 自发光

类似生活中的萤火虫等自己能够发光

* 高光反射

类似生活中的镜子,近似认为百分百反射出去

* 漫反射

类似生活中的光照射到墙壁上、桌子上的反光不会百分百反射出去,各个方向都会反射。

* 环境光

类似生活中的光照照射在某个物体上,物体漫反射然后反射到其他物体上这些过程的光是环境光。

以上都是模拟生活中的光照效果,并不等同于实际生活中的光照计算,实际生活中的光照会更加复杂。

光照模型中关于漫反射的计算

漫反射计算放在顶点函数里面就是逐顶点光照

Disffuse = 直射光颜色*max(0,cosθ) (θ是法线跟光照的夹角) 如果夹角小于0的话就是背光面就直接取黑色

理论知识:

  • 向量点乘(结果值)

    a*b=|a||b|cosθ

  • 向量叉乘(结果是向量)

    a*b=c c的一个向量,值是|a||b|sinθ

编写漫反射Shader

在Pass块里添加Tags{“LightMode”=”ForwardBase”}和#include “Lighting.cginc” //引用一些写好的程序块 会包含一些获取光照的信息

只有定义了正确的LightMode才能得到一些Unity的内置光照变量。

  • normalize() 将一个向量单位化
  • max()用来取得函数中最大的一个
  • dot()用来取得两个向量的点积
  • _WorldSpaceLightPos0取得平行光的位置
  • _LightColor0取得平行光的颜色
    UNITY_MATRIX_MVP 这个矩阵是将模型空间转换到剪裁空间
  • _World2Object这个矩阵用来将一个方向从时间空间转换到模型空间
  • UNITY_LIGHTMODEL_AMBIENT 用来获取环境光
Shader "AladdinShader/04 Diffuse Tertex Shader"
{
    Properties
    {

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

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

#pragma vertex vert
#pragma fragment frag 

        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 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir));//取得第一个直射光的颜色
            f.color = diffuse;
            return f;
        }

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

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果:
没有写反射光照之前的效果,红色是均匀的

写了光照效果之后,会看到正对光照地方很亮,背对光照的地方就很暗甚至黑色。

现在是逐顶点光照,顶点的数量是有限的,如果将计算放在片元函数里面计算效果会更好,但计算量也会更大,那就叫逐片元光照。

给物体自身添加颜色和环境光共同控制

上面物体的颜色只是受环境光的颜色影响,但实际生活中我们看到的物体的颜色是环境光和自身颜色混合的效果。我们在上面的例子的基础上,添加上物体本身的颜色:
给物体添加颜色属性然后*环境光

Shader "AladdinShader/04 Diffuse Tertex 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 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获得系统内置的环境光

            //获得世界空间的单位法线向量
            fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _Diffuse.rgb;//取得第一个直射光的颜色 颜色融合
            f.color = diffuse + ambient;  //颜色增加 增强
            return f;
        }

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

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果:

这样直射光设置成白色,颜色本身设置成绿色,并且在面板中调整颜色,物体就显示成这种颜色。颜色融合一般是颜色的改变,颜色相加是强度增强。

环境光设置:Window->Lighting->Ambient Source->Color 一般不建议很强的环境光

逐像素光照

Shader "AladdinShader/05 Diffuse fragment 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 worldNormalDir:COLOR0;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获得系统内置的环境光

            //获得世界空间的单位法线向量
            fixed3 normalDir = normalize(f.worldNormalDir); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _Diffuse.rgb;//取得第一个直射光的颜色 颜色融合
            fixed3 tempColor = diffuse + ambient;  //颜色增加 增强
            return fixed4(tempColor,1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}

效果图:

一个是逐顶点光照,一个是逐像素光照,自己看还是能看出区别的。逐像素光照会更消耗性能一些,如果不需要很高的精度的话就可以用逐顶点光照代替。

半兰伯特光照模型

模型表面的明亮度直接取决于光线向量(light vector)和表面法线(normal)两个向量将夹角的余弦值。

如果漫反射光强设置为Diffuse,入射光光强为I,光方向和法线夹角为θ,那么兰伯特光照模型可以用下面的公式表示:Diffuse = I * cosθ。
兰伯特光照模型 模型背面全都是黑色都看不清楚

为了解决这个问题,改造一些兰伯特光照模型的公式,让背光部分也随着光照强度改变而改变。
半兰伯特光照模型:
为了解决兰伯特模型背光面全都是黑的看不清楚的问题。
Diffuse=直射光颜色 * (cosθ * 0.5 + 0.5)

Shader修改

Shader "AladdinShader/06 Diffuse fragment Half Lanbote 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 worldNormalDir:COLOR0;
        };
        v2f vert(a2v v)
        {
            v2f f;
            f.position = mul(UNITY_MATRIX_MVP, v.vertex);
            f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
            return f;
        }

        fixed4 frag(v2f f):SV_Target
        {
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//获得系统内置的环境光

            //获得世界空间的单位法线向量
            fixed3 normalDir = normalize(f.worldNormalDir); //从模型空间转到世界空间 float3x3 将一个4*4的矩阵强转成3*3的矩阵

            //世界空间下的光照位置
            fixed3  lightDir = _WorldSpaceLightPos0.xyz; //对于每一个点来说每一个光的位置就是光的方向(针对平行光)

            float halfLambert = dot(normalDir, lightDir) * 0.5 + 0.5; //半兰伯特光照模型
            //取得漫反射的颜色
            fixed3 diffuse = _LightColor0.rgb * halfLambert * _Diffuse.rgb;//取得第一个直射光的颜色 颜色融合
            fixed3 tempColor = diffuse + ambient;  //颜色增加 增强
            return fixed4(tempColor,1);
        }

        ENDCG
        }
    }
    FallBack  "VertexLit"
}


会明显的感觉模型亮度变亮了,而且背面也不会纯黑了也会渐变,而且正面颜色也会鲜明一些,这样整体效果会更好一些。

时间: 2024-09-13 20:54:53

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

【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"="Forward

【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渲染流程中发

CUDA编程(二) CUDA初始化与核函数

CUDA编程(二) CUDA初始化与核函数 CUDA初始化 在上一次中已经说过了,CUDA安装成功之后,新建一个工程还是十分简单的,直接在新建项目的时候选择NVIDIA CUDA项目就可以了,我们先新建一个MyCudaTest 工程,删掉自带的示例kernel.cu,然后新建项,新建一个CUDA C/C++ File ,我们首先看一下如何初始化CUDA,因此我命名为InitCuda.cu 首先我们要使用CUDA的RunTime API 所以 我们需要include cuda_runtime.h

qml-QML中使用shader编程达到光照效果

问题描述 QML中使用shader编程达到光照效果 在用QML做界面工作,现在有个需求是灯光效果参照digia公司的例子做的修改(图片在这还没弄明白怎么上传,后面的网址上有图片信息:http://bbs.csdn.net/topics/390712006),最终要达到的效果是图片中红色矩形框不要,只要里面的黄色区域的光点.也就是说在窗口中有个pathView光点直接打到pathView的每个item上.这个需求是否可以做到?应该怎么做? 当然铺满全屏看上去就没有矩形框了,但是那样使得界面内所有的

iOS网络编程之二——NSURLSession的简单使用

iOS网络编程之二--NSURLSession的简单使用 一.NSURLSession简介     在iOS7之后,NSURLSession作为系统推荐使用的HTTP请求框架,在进行前台请求的情况下,NSURLSession与NSURLConnection并无太大差异,对于后台的请求,NSURLSession更加灵活的优势就将展现无遗.         1.NSURLSession集合的类型         NSURLSession类提供3中Session类型:         Default类

Android 开发者如何使用函数式编程 (二)

本文讲的是Android 开发者如何使用函数式编程 (二), 如果你没有读过第一部分,请到这里读: Android 开发者如何使用函数式编程 (一) 在上一篇帖子中,我们学习了纯粹性*.副作用和排序**.在本部分中,我们将讨论不变性和并发. 不变性 不变性是指一旦一个值被创建,它就不可以被修改. 假设我有一个像这样的 Car 类: public final class Car { private String name; public Car(final String name) { this.