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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集