Unity中Shader光照模型Blinn-Phong原理及实现

2023-10-13 17:52

本文主要是介绍Unity中Shader光照模型Blinn-Phong原理及实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 一、Blinn-Phong原理
  • 二、Blinn-Phong实现
    • 最终代码


前言

Unity中Shader光照模型Blinn-Phong原理及实现,也是经验型光照模型。和Phong模型一样,都是用于实现高光效果


一、Blinn-Phong原理

在这里插入图片描述

可以看出:Blinn-Phong模型和Phong模型不同的地方在于,点积时的 N 和 H 向量

Phong模型:
Specular = SpecularColor * Ks * pow(max(0,dot(R,V)),Shininess)
Blinn-Phong模型:
Specular = SpecularColor * Ks * pow(max(0,dot(N,H)),Shininess)

半角向量的计算方法
在这里插入图片描述
半角向量 = 向量1 + 向量2
即 H = L+ V

二、Blinn-Phong实现

在上一篇 Phong 模型的基础上,进行如下修改即可:

fixed3 H = normalize(L + V);
fixed4 BlinnSpecular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);

先输出一下 BlinnPhong 结果看一下:请添加图片描述

最终代码

Shader "MyShader/P1_5_8"
{Properties{[Header(Diffuse)]//光照系数_DiffuseIntensity("Diffuse Intensity",float) = 1[Header(Specular)]//高光颜色_SpecularColor("Specular Color",Color) = (1,1,1,1)//高光系数_SpecularIntensity("Specular Intensity",Float) = 1//高光范围系数_Shininess("Shininess",Float) = 1}SubShader{Tags { "RenderType"="Opaque" }Pass{Tags{"LightMode"="ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;//在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息half3 normal:NORMAL;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;//定义一个3维向量,用于接受世界坐标顶点法向量信息half3 worldNormal:TEXCOORD1;//用于存储模型顶点的世界坐标float3 worldPos : TEXCOORD2;};half _DiffuseIntensity;fixed4 _SpecularColor;float _SpecularIntensity,_Shininess;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);//把顶点法线本地坐标转化为世界坐标o.worldNormal = UnityObjectToWorldNormal(v.normal);//把模型的顶点坐标从本地坐标转化到世界坐标o.worldPos = mul(unity_ObjectToWorld,v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{//Lambert光照模型的结果//Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L))//使用 Unity 封装的参数 获取环境光色float Ambient = unity_AmbientSky;//在属性面板定义一个 可调节的参数 用来作为光照系数,调节效果的强弱half Kd = _DiffuseIntensity;//获取主平行光的颜色fixed4 LightColor = _LightColor0;//获取顶点法线坐标(让其归一化)fixed3 N = normalize(i.worldNormal);//获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)fixed3 L = _WorldSpaceLightPos0;//使用Lambert公式计算出光照//fixed4 Diffuse = Ambient + (Kd * LightColor * dot(N,L));//因为 当 顶点法线 与 反射点指向光源的向量 垂直 或成钝角时,光照效果就该忽略不计//所以,这里使用 max(a,b)函数来限制 点积的结果范围fixed4 Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L));//return Diffuse;//Phong模型公式//Specular = SpecularColor * Ks * pow(max(0,dot(R,V)), Shininess)// 获取 V (模型顶点的世界坐标 指到 到摄像机世界坐标的单位向量)fixed3 V = normalize(_WorldSpaceCameraPos - i.worldPos);//使用之前计算得到的公式//fixed3 R = 2 * dot(N,L) * N - L;//使用自带的计算反射光的函数fixed3 R = reflect(-L,N);fixed4 Specular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(R,V)),_Shininess);//BlinnSpecular = SpecularColor * Ks * pow(max(0,dot(N,H)), Shininess)fixed3 H = normalize(L + V);fixed4 BlinnSpecular = _SpecularColor  * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);return BlinnSpecular+Diffuse;}ENDCG}Pass{Tags{"LightMode"="ForwardAdd"}Blend One OneCGPROGRAM#pragma vertex vert#pragma fragment frag//加入Unity自带的宏,用于区分不同的光照//只声明我们需要的变体//#pragma multi_compile POINT SPOT#pragma multi_compile_fwdadd//剔除我们不需要的变体#pragma skip_variants DIRECTIONAL POINT_COOKIE DIRECTIONAL_COOKIE#include "UnityCG.cginc"#include "Lighting.cginc"//使用光照衰减贴图,需要引入 AutoLight.cginc 库#include "AutoLight.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;//在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息half3 normal:NORMAL;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;//定义一个3维向量,用于接受世界坐标顶点法向量信息half3 worldNormal:TEXCOORD1;//定义一个三维向量,用于存放模型顶点 从本地坐标 转化为 世界坐标float3 worldPos : TEXCOORD2;};half _DiffuseIntensity;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;//把顶点法线本地坐标转化为世界坐标o.worldNormal = UnityObjectToWorldNormal(v.normal);//把模型顶点从本地坐标转化为世界坐标o.worldPos = mul(unity_ObjectToWorld,v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{/*#if POINTreturn fixed4(0,1,0,1);#elif SPOTreturn 0;#endif*///把模型顶点从世界坐标转化为灯光坐标//unity_WorldToLight//从世界空间转换到灯光空间下,等同于旧版的_LightMatrix0//因为转化时使用的是4行的矩阵,所以 要把模型的顶点坐标增加一个w = 1,使坐标转化准确//float3 lightCoord = mul(unity_WorldToLight,float4(i.worldPos,1)).xyz;//return lightCoord.x;//使用Unity自带的光照衰减贴图进行纹理采样//fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord));//使用Unity自带的方法实现光照衰减UNITY_LIGHT_ATTENUATION(atten,0,i.worldPos)//获取主平行光的颜色fixed4 LightColor = _LightColor0 * atten;//获取顶点法线坐标(让其归一化)fixed3 N = normalize(i.worldNormal);//获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)fixed3 L = _WorldSpaceLightPos0;//因为计算点光源时不需要考虑环境光,所以在Lambert光照模型中删除环境光的影响fixed4 Diffuse = LightColor * max(0,dot(N,L));return Diffuse;}ENDCG}}
}

最终效果:
请添加图片描述

这篇关于Unity中Shader光照模型Blinn-Phong原理及实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/204918

相关文章

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Mysql实现范围分区表(新增、删除、重组、查看)

《Mysql实现范围分区表(新增、删除、重组、查看)》MySQL分区表的四种类型(范围、哈希、列表、键值),主要介绍了范围分区的创建、查询、添加、删除及重组织操作,具有一定的参考价值,感兴趣的可以了解... 目录一、mysql分区表分类二、范围分区(Range Partitioning1、新建分区表:2、分

MySQL 定时新增分区的实现示例

《MySQL定时新增分区的实现示例》本文主要介绍了通过存储过程和定时任务实现MySQL分区的自动创建,解决大数据量下手动维护的繁琐问题,具有一定的参考价值,感兴趣的可以了解一下... mysql创建好分区之后,有时候会需要自动创建分区。比如,一些表数据量非常大,有些数据是热点数据,按照日期分区MululbU

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

IDEA中新建/切换Git分支的实现步骤

《IDEA中新建/切换Git分支的实现步骤》本文主要介绍了IDEA中新建/切换Git分支的实现步骤,通过菜单创建新分支并选择是否切换,创建后在Git详情或右键Checkout中切换分支,感兴趣的可以了... 前提:项目已被Git托管1、点击上方栏Git->NewBrancjsh...2、输入新的分支的

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互