【ShaderLab】Phong与Blinn-Phong两个光照模型的理解

2023-11-05

正文

        (顺手抄来一个图)

Phong与Blinn-Phong两个光照模型都是用来实现光照射在物体上,物体表现产生一个高光部分的效果的,两个模型也十分相似,毕竟后者是对前者进行了一个计算上的优化。虽然说两个光照模型都有了现成的代码函数封装,尤其是shaderforge插件跟新版本unity的shadergraph这两个东西,都能够直接拖拽几个图形化控件就能实现要说的这两个光照模型效果,简直是相当过份了。但理解原理之后,被别人问起来心都不会觉得虚。

 

Phong光照模型

理想情况下,光源射出的光线,通过镜面反射,正好在反射光方向观察,观察者可以接受到的反射光最多,那么观察者与反射方向之间的夹角就决定了能够观察到高光的多少。夹角越大,高光越小,夹角越小,高光越大。而另一个影响高光大小的因素是表面的光滑程度,表面越光滑,高光越强,表面月粗糙,高光越弱。L代表光源方向,N代表顶点法线方向,V代表观察者方向,R代表反射光方向。首先需要计算反射光的方向R,反射光方向R可以通过入射光方向和法向量求出,R + L = 2dot(N,L)N,进而推出R = 2dot(N,L)N - L。(至于如何推算出这个公式,CSDN有不少的博客都有笔记介绍,就是一个简单的向量与三角函数转换,不懂的可以百度下或者直接看这里

(再顺手抄来一个图)

 

Blinn-Phong光照模型

 

Phong光照模型能够很好地表现高光效果,不过Blinn-Phong光照的缺点就是计算量较大,所以,在1977年,Jim Blinn对Phong光照进行了改进,称之为Blinn-Phong光照模型。

Blinn-Phong光照引入了一个概念,半角向量,用H表示。半角向量计算简单,通过将光源方向L和视线方向V相加后归一化即可得到半角向量。Phong光照是比较反射方向R和视线方向V之间的夹角,而Blinn-Phong改为比较半角向量H和法线方向N之间的夹角。半角向量的计算复杂程度要比计算反射光线简单得多,所以Blinn-Phong的性能要高得多,效果比Phong光照相差不多,所以OpenGL中固定管线的光照模型就是Blinn-Phong光照模型。

BlinnPhong光照模型的计算公式如下:

I(spcular) = I * k * pow(max(0,dot(N,H)), gloss) ,其中I为入射光颜色向量,k为镜面反射系数,gloss为光滑程度。

==================================================================

下面贴上两个光照模型的shaderlab代码

Shader "Custom/Phong" {

Properties{

_Specular("Specular", Color) = (1,1,1,1)

_Diffuse("Diffuse",Color) = (1,1,1,1)

_Gloss("Gloss",Range(1,255)) = 10

}

SubShader{

Pass{

Tags {"LightMode" = "ForwardBase" }

LOD 200



CGPROGRAM

#include "Lighting.cginc"

#pragma vertex vert

#pragma fragment frag



fixed4 _Diffuse;

fixed4 _Specular;

float _Gloss;



struct a2v

{

float4 vertex : POSITION;

float3 normal : NORMAL;

};

struct v2f

{

float4 pos:SV_POSITION;

float3 worldNormal:NORMAL;

float3 worldPos:TEXCOORD1;

};



v2f vert(a2v v)

{

v2f o;

//矩阵变换 获取投影坐标下的顶点

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

//获取世界坐标下的法线坐标

o.worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));

//o.worldNormal=normalize(mul((float3x3)_Object2World,v.normal));//与上面等价

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

return o;

}



fixed4 frag(v2f i) :SV_Target

{

//获取环境光

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*_Diffuse;

//获取灯光方向 并归一化

fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

//归一化世界坐标下的法线

fixed3 worldNormal = normalize(i.worldNormal);

//获取漫反射

fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLight));

//获取反射光的方向 入射光方向为-worldLight,通过reflect函数获取反射光方向

fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));

//获取点在摄像机的观察位置,并归一化 (相机坐标-像素位置)

fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

//phong的高光公式

fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(0, dot(reflectDir, viewDir)), _Gloss);

fixed3 color = diffuse + ambient + specular;

return fixed4(color, 1.0);

}

ENDCG

}

}

FallBack "Specular"

}

 

======================================================================

Shader "Custom/Blinn_Phong" {

Properties{

_Specular("Specular",color) = (1,1,1,1)

_Diffuse("Diffuse",color) = (1,1,1,1)

_Gloss("Gloss",Range(1,100)) = 20

}

SubShader{

Pass{

Tags { "RenderType" = "Opaque" }

LOD 200

CGPROGRAM

#include "Lighting.cginc"

#pragma vertex vert

#pragma fragment frag



float3 _Specular;

float3 _Diffuse;

float _Gloss;



struct a2v {

float4 vertex:POSITION;

float3 normal:NORMAL;

};

struct v2f {

float4 pos:SV_POSITION;

float3 worldNormal:NORMAL;

float3 worldPos:TEXCOORD0;

};



v2f vert(a2v a)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, a.vertex);

o.worldNormal = mul(_Object2World, a.normal);

o.worldPos = mul(_Object2World, a.vertex).xyz;

return o;

}



float4 frag(v2f i):SV_Target

{

//获取环境光

float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*_Diffuse;

//获取灯光 并且归一化

float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

//获取法线并归一化

float3 worldNormal = normalize(i.worldNormal);

//获取视线方向(摄像头位置-像素对应位置) 并归一化

float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

//获取漫反射灯光

float3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLight));

//获取半角向量(光线方向+视线方向) 并归一化

float3 halfDir = normalize(worldLight + viewDir);

//高光公式

float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(halfDir, worldNormal)),_Gloss);

float3 color = ambient + diffuse + specular;

return float4(color,1.0);

}

ENDCG

}

}

FallBack "Diffuse"

}

 

=======================================================================================

代码里已经有对部分关键代码进行注释了,就不过多赘述。而Blinn-Pong是引入了一个半角向量来代替反射的向量,所以着色器的运算量少了挺多,下面就直接对Blinn-Phong的着色器代码进行总结。

Blinn-Pong光照着色器

引用光照模型"Lighting.cginc"

定义a2v结构体,要获取模型的顶点与法线

定义v2f结构体,存储顶点信息(SV_POSITION) 世界法线 和顶点在世界坐标的位置

 

顶点函数里需要获取

顶点的投影坐标

顶点的世界法线

顶点的世界坐标

 

片段函数里需要获取

环境光,需要将光照模型中的环境光(UNITY_LIGHTMODEL_AMBIENT.xyz)与Diffuse相乘

归一化世界法线

归一化光照位置(_WorldSpaceLightPos0.xyz)

归一化点的视角位置(_WorldSpaceCameraPos.xyz-worldPos)

漫反射(_LightColor0.rgb*Diffuse.rgb*saturate(dot(worldNormal,lightDir)))

归一化半角向量(viewDir+lightDir)

最后的高光公式LightColor0.rbg*_Specular.rbg*pow(dot(halfDir,worldNormal),_Gloss)

返回 高光+环境光+漫反射光

总结

        这两个光照模型其实都有一个中文名,就叫冯光照模型与布林-冯光照模型,但换成了中文后感觉就拗口了不少啊。从理解上的话Phong模型可以更直观地推出结论,而后者的一个半角向量我也不知道是怎么推算出来的,但少了一个点乘运算感觉代码都简洁了不少,有种前人种树后人荫的爽快感。

        偷懒了快一年最近才有空憋出了这一编个人理解,看来持之以恒还是一个不容易实现的词语啊。虽然平时也有做笔记的习惯,但感觉记了笔记就放在那里了,时间一久就基本忘光,回头看一看也得楞个很久才能悟过来,这样写写博客也算是对自己的记忆来一个回顾吧,还是希望能继续坚持下去吧。。。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【ShaderLab】Phong与Blinn-Phong两个光照模型的理解 的相关文章

  • 使用 python opengl (PyOpenGL) 创建几何着色器失败

    我想使用 glCreateShader GL GEOMETRY SHADER 创建 Geometry shader 但出现错误 Traceback most recent call last File test py line 9 in
  • QML 将纹理应用于网格

    我正在尝试将图像纹理应用到 QML Qt 5 6 2 中的网格 我从示例 Shadow Map QML 开始 我想对 GroundPlane 进行纹理处理 材质和效果 qml 类应用于该 GroundPlane 网格 但我看不到如何应用图像
  • Qt3D默认制服和属性

    我开始学习通过 QML 使用着色器 但找不到任何讨论传递给着色器的默认统一和属性值的参考资料 在某些示例中 我们可以看到其中的几个 例如顶点位置 or 模型视图投影 这也被传递为mvp 但是没有包含我们可以使用的所有变量的清晰列表 在调查
  • 使用 LibGDX 的法线贴图 GLSL

    我尝试使用 LibGDX 实现法线贴图 因此 当我在顶点着色器中计算漫反射和镜面反射颜色时 我得到了一些积极的结果 至少我这么认为 顶点着色器 attribute vec4 a position attribute vec2 a texCo
  • 处理中点/笔划的景深着色器

    最近我一直在使用下面的景深着色器 最初来自ofx后处理 https github com neilmendoza ofxPostProcessing blob master src DofPass cppOpenFrameworks 库 用
  • WPF 着色器效果 - 抗锯齿不显示

    我遇到一个问题 我有一个 WPF 着色器效果 由 Rene Schulte 修改 来模拟点阵显示 DMD 一切都很好 但所有的点都是别名的 See attached image 我尝试了WPF中的许多功能 以带来抗锯齿 但无济于事 在构造函
  • 如何绘制存储在 SSBO 中的顶点?

    这是下面的一个问题OpenGL 和加载 读取 AoSoA 混合 SoA 格式的数据 https stackoverflow com questions 59616117 opengl and loading reading data in
  • 使用 numpy 进行 blinn-phong 着色

    我正在尝试在 numpy 中实现 blinn phong 着色以用于教育目的 然而 我几天来一直在调试参数的作用 我的总体想法如下 由于方程是针对通道给出的 我将模型应用于每个颜色通道以获得通道中的相对像素强度 然后将通道重新组合在一起以获
  • GLSL 纹理立方体和纹理2D 在同一着色器中

    我似乎无法两者兼得texture2D and textureCube 在一个着色器中 当我这样做时 什么也没有显示 也没有错误 我用我自己的着色器加载器和 Apple GLSL 着色器生成器尝试了这一点 并且发生了同样的事情 即使我有也会发
  • 如何将shadershop公式转换成glsl

    我最近一直在学习着色器的一些基础知识 并且想出了一个很棒的视觉工具 着色器商店 http www cdglabs org Shadershop 但我无法将我在此站点中创建的公式转换为 glsl 一个简单的例子 我在此网站中创建了一个公式 我
  • OpenGL 统一缓冲区 std140 布局

    我正在尝试通过 GeForce 8600 GT 上的统一块将整数数组传递给片段着色器 一切均根据 GLSL version 330 在应用程序方面我有 int MyArray 7102 filling binding etc glBuffe
  • OpenGL:多个顶点的单个顶点属性?

    我有一个接受以下属性的顶点着色器 a posCoord 顶点位置 a texCoord 纹理坐标 传递给片段着色器 a alpha 透明度因子 传递给片段着色器 我正在渲染的对象都是 广告牌 一对直角三角形组成一个矩形 我正在使用一次调用g
  • Unity 后处理 PostProcessEffectRenderer 在编辑器中显示,但在构建中不显示

    将 PostProcessEffectRenderer 的实现添加到 Unity 后处理堆栈后 效果在 Unity 编辑器中完美运行 但不会在构建的游戏中显示 对构建质量的更改没有效果 使用针对 Windows x86 64 构建的最高质量
  • CPU 到 GPU 法线映射

    我正在创建一个地形网格 然后这个答案 https stackoverflow com a 5284527 1356106我正在尝试将 CPU 计算法线迁移到基于着色器的版本 以便通过降低网格分辨率并使用在片段着色器中计算的法线贴图来提高性能
  • Unity3D:在 AA 解析后绘制粒子以提高性能

    我正在尝试评估 MSAA 对 Unity 中含有大量粒子的场景的影响 为此 我需要 使用 8x MSAA 绘制场景中的所有非粒子对象 使用上一个通道中解析的深度缓冲区来渲染所有 将非遮挡粒子系统转移到较小的渲染目标上 将 2 的颜色缓冲区与
  • 使用 OpenGL 着色器进行数学计算 (C++)

    我有一个矩阵 例如 100x100 尺寸 我需要对每个元素进行计算 matrix i j tt 8 5例如 我有一个巨大的矩阵 我想使用 OpenGL 着色器来实现该算法 我想使用着色器 例如 uniform float val unifo
  • 丢弃对 OpenGL 中的程序性能有影响吗?

    我正在读书this http code google com p gdc2011 android opengl wiki TalkTranscript文章 作者写道 以下是如何通过两个简单的步骤在每个平台上编写高性能应用程序 遵循最佳实践
  • Three.js、自定义着色器和具有透明度的 png 纹理

    我有一个非常简单的 PNG 纹理 一个带有透明背景的灰色圆圈 我用它作为制服map for a THREE ShaderMaterial var uniforms THREE UniformsUtils merge basicShader
  • 使用 gl_FragColor 与 vec4 颜色?

    似乎有很多不明确的地方gl FragColor被弃用 例如 它缺失在GLSL 4 40 规范 https www khronos org registry OpenGL specs gl GLSLangSpec 4 40 pdf 但它包含在
  • Shader的功能参数表现

    我试图了解如何在着色器语言中实现传递参数 我读过几篇文章和文档 但仍然有一些疑问 特别是我试图理解与C 函数调用 特别强调性能 之间略有差异HLSL Cg and GLSL但我猜下划线的实现非常相似 到目前为止我所理解的 除非另有说明 函数

随机推荐