unity-shader-PBR基于物理渲染

2023-11-29 22:48
文章标签 unity 渲染 物理 shader pbr

本文主要是介绍unity-shader-PBR基于物理渲染,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载:https://blog.csdn.net/yangxuan0261/article/details/88749969

 

title: unity-shader-PBR基于物理渲染
categories: Unity3d-Shader
tags: [unity, shader, pbr, ta]
date: 2019-03-07 10:13:21
comments: false
PBR : Physically based rendering, 基于物理的渲染。 暂时只有直接光照部分。

前篇
learnopengl 中文站的 PBR 知识, 包含 PBR 所有的方程使用姿势, 引起极度舒适.
pbr 理论 - https://learnopengl-cn.github.io/07 PBR/01 Theory/
光照 - https://learnopengl-cn.github.io/07 PBR/02 Lighting/
英文原版官网 : https://learnopengl.com/PBR/Lighting
Unity Shader学习笔记(31)基于物理的渲染技术(PBS)、BRDF - http://gad.qq.com/article/detail/38388
Unity PBR Workflow-2-PBR in Unity - https://www.youtube.com/watch?v=Q0G1SMaeEwI
Create PBR Surface In Unity 5 - https://www.youtube.com/watch?v=qH63_l8DVfI
PBR材质]锈迹材质全流程实例:Blender-》SP-》UE4 - https://zhuanlan.zhihu.com/p/34764973
探究PBR的两种流程以及Unity中的PBS - http://richbabe.top/2018/06/25/探究PBR的两种流程以及Unity中的PBS/
Unity的PBR扩展——皮毛材质 - https://zhuanlan.zhihu.com/p/57897827
Unity3D手游项目的总结和思考(2) - 角色渲染 ( 实现pbr的shader, 带溶解等效果 ) - https://blog.csdn.net/qq18052887/article/details/80375546
关于PBR贴图的那些事儿 - http://www.tenjoyedu.com/news/99.html
浅谈PBR在手游开发中的适用性 - https://blog.uwa4d.com/archives/TechSharing_109.html
什么是PBR - http://geekfaner.com/unity/blog5_PBR.html
猴子都能看懂的PBR - https://zhuanlan.zhihu.com/p/33464301
unity standard shader ( 深度好文 ) - http://geekfaner.com/unity/blog16_UnityStandardShader.html
PBR - http://geekfaner.com/unity/blog5_PBR.html
PBR物理渲染的基本原理及与传统渲染的异同 ( good ) - https://www.vive.com/cn/forum/2128
(good) substance 的 pbr-guide - https://academy.allegorithmic.com/courses/the-pbr-guide-part-1
中文翻译版
模型贴图师宝典:PBR 综合指南 - Vol1《光与物质:创建PBR纹理的实践指南》
模型贴图师宝典:PBR 综合指南 - Vol2《光与物质:创建PBR纹理的实践指南》
浅墨的游戏编程
【基于物理的渲染(PBR)白皮书】(一) 开篇:PBR核心知识体系总结与概览
【基于物理的渲染(PBR)白皮书】(二) PBR核心理论与渲染光学原理总结
【基于物理的渲染(PBR)白皮书】(三)迪士尼原则的BRDF与BSDF相关总结
PBR 工作流
PBR工作流(笔记) - https://www.jianshu.com/p/67321efbdaac
PBR概述 - https://www.cnblogs.com/guanzz/p/7416790.html
pbr的三个条件
判断一种PBR光照模型是否是基于物理的,必须满足以下三个条件(不用担心,我们很快就会了解它们的):

基于微平面(Microfacet)的表面模型。
能量守恒。
应用基于物理的BRDF。
漫反射 与 镜面反射
​ 平面散射光有两种:

进入平面的部分( 漫反射 )
从平面出去的部分( 镜面反射 )
光与平面的交互一种是平面直接反射出去的部分,我们成为 镜面反射,这部分其实就是美术常用的 specular 。另一部分是传到物质内部,经过折射被物质吸收的部分,或者内部进行散射,一些散射的光会最终重新返回平面折射出来,我们称为 漫反射,也就是 diffuse 。

漫反射光 被物质吸收并散射后,会成为不同波长的光。前面我们说过,真实的物理世界是没有颜色属性的,颜色只是人眼的感知。正是漫反射光被人眼感知后,“赋予”了物体颜色。例如物体吸收了蓝色以外的光,那物体就是蓝色的,所以可以说漫反射决定了物体基本的颜色,这也是为什么我们用diffuse贴图来表达纹理。(传统的渲染方式,由于全部使用漫反射贴图这一张贴图来模拟光的信息,所以一般情况下,我们会将AO,也就是环境光遮罩,直接绘制在diffuse上,但是对于PBR来说,由于整个引擎渲染使用的都是物理渲染管线。所以在绘制diffuse贴图时,AO信息可以不画,或者极少。)

能量守恒
能量守恒,是PBR与传统渲染最大的区别。所谓能量守恒,简单的理解就是镜面反射出去的光与漫反射出去的光,加起来总量不能超过入射的光量,也就是 diffuse+specular 不能大于1。如果你希望你的材质有较高的反射率,那就要同时降低漫反射的强度。在真实的物理世界中也是如此。

shader 中由 ks 推导出 kd

vec3 kS = F; // F 是菲涅尔
vec3 kD = vec3(1.0) - kS; // 能量守恒
1
2
BRDF 的 反射率方程 公式


c 表示表面颜色, albedo

入射(光)方向 ωi,出射(观察)方向 ωo

Lo : 看到的颜色最终值, 表示了从 ωo 方向上观察,光线投射到点 p 上反射出来的辐照度。

D : 正态分布函数 ( Normal Distribution Function, 简称 ndf )

F : 菲涅尔方程 ( Fresnel Rquation )

G : 几何函数( Geometry Function )

ks : 也就是 F ( 在实际运算中不用乘以这个 ks, 因为在 DFG 中已经乘过了 )

kd : 1 - ks, 能量守恒

Li : 光的颜色 ( 也就是辐照度, 经过衰减的颜色 )

关于多光源计算
这也让我们回到了对于表面的半球领域(hemisphere) Ω 的积分 ∫ 上。由于我们事先知道的所有贡献光源的位置,因此对物体表面上的一个点着色并不需要我们尝试去求解积分。我们可以直接拿光源的(已知的)数目,去计算它们的总辐照度,因为每个光源仅仅只有一个方向上的光线会影响物体表面的辐射率。这使得PBR对直接光源的计算相对简单,因为我们只需要有效地遍历所有有贡献的光源。而当我们后来把环境照明也考虑在内的IBL教程中,我们就必须采取积分去计算了,这是因为光线可能会在任何一个方向入射。

vec3 Lo = vec3(0.0); // 出射光, 也就是看到的物体颜色 (不含环境光)
for(int i = 0; i < 4; ++i) // 4 是光源个数
{
    Lo += (kD * albedo / PI + specular) * radiance * NdotL; // albedo 参与
}
1
2
3
4
5
Cook-Torrance BRDF
Cook-Torrance BRDF的镜面反射部分包含三个函数,此外分母部分还有一个标准化因子 。字母D,F与G分别代表着一种类型的函数,各个函数分别用来近似的计算出表面反射特性的一个特定部分。三个函数分别为正态分布函数(Normal Distribution Function),菲涅尔方程(Fresnel Rquation)和几何函数(Geometry Function):

D : 正态分布函数 ( Normal Distribution Function, 简称 ndf ) :估算在受到表面粗糙度的影响下,取向方向与中间向量一致的微平面的数量。这是用来估算微平面的主要函数。
G : 几何函数( Geometry Function ) :描述了微平面自成阴影的属性。当一个平面相对比较粗糙的时候,平面表面上的微平面有可能挡住其他的微平面从而减少表面所反射的光线。
F : 菲涅尔方程 ( Fresnel Rquation ) :菲涅尔方程描述的是在不同的表面角下表面所反射的光线所占的比率。


下面公式中的 h 是 半角向量, 也就是 vec3 H = normalize(V + L)

D - 正态分布函数


在这里 h 表示用来与平面上微平面做比较用的 中间向量 ( 也叫半角向量 ) ,而 a 表示表面 粗糙度

float DistributionGGX(vec3 N, vec3 H, float roughness) // roughness 参与
{
    float a      = roughness*roughness;
    float a2     = a*a;
    float NdotH  = max(dot(N, H), 0.0);
    float NdotH2 = NdotH*NdotH;

    float nom   = a2;
    float denom = (NdotH2 * (a2 - 1.0) + 1.0);
    denom = PI * denom * denom;

    return nom / denom;
}
1
2
3
4
5
6
7
8
9
10
11
12
13


当α非常接近0的时候,光照集中在一点,其他方向会完全看不到光线。

F - 菲涅尔方程


通过预先计算电介质与导体的 F0 值,我们可以对两种类型的表面使用相同的Fresnel-Schlick近似,但是如果是 金属 表面的话就需要对基础 反射率 添加色彩。我们一般是按下面这个样子来实现的:

vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

vec3 F0 = vec3(0.04); 
F0      = mix(F0, albedo, metallic); // albedo 参与, metallic 参与
vec3 F  = fresnelSchlick(max(dot(H, V), 0.0), F0);
1
2
3
4
5
6
7
8
F0 材料对应值表
shader 中的 F0 参数根据物体的材料属性填入对应的值会显得更接近于真实世界.

G - 几何函数


k 由 a ( 粗糙度 ) 求得

float GeometrySchlickGGX(float NdotV, float roughness) // roughness 参与
{
    float r = (roughness + 1.0);
    float k = (r*r) / 8.0;

    float nom   = NdotV;
    float denom = NdotV * (1.0 - k) + k;

    return nom / denom;
}


1
2
3
4
5
6
7
8
9
10
11
12
为了有效的估算几何部分,需要将观察方向(几何遮蔽(Geometry Obstruction))和光线方向向量(几何阴影(Geometry Shadowing))都考虑进去。我们可以使用史密斯法(Smith’s method)来把两者都纳入其中:

float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) // roughness 参与
{
    float NdotV = max(dot(N, V), 0.0);
    float NdotL = max(dot(N, L), 0.0);
    float ggx2  = GeometrySchlickGGX(NdotV, roughness);
    float ggx1  = GeometrySchlickGGX(NdotL, roughness);

    return ggx1 * ggx2;
}
1
2
3
4
5
6
7
8
9


效果就是粗糙度越大,亮度越低。但视线和光线越接近垂直,受粗糙度的影响就越小

能量守恒
能量守恒:出射光线的能量永远不能超过入射光线的能量(发光面除外)。当一束光线碰撞到一个表面的时候,它就会分离成一个折射部分和一个反射部分。反射部分就是会直接反射开来而不会进入平面的那部分光线,这就是我们所说的镜面光照(高光)。而折射部分就是余下的会进入表面并被吸收的那部分光线,这也就是我们所说的漫反射光照。能量守恒公式:漫反射+镜面反射= 1

现在我们终于可以计算每个光源在反射率方程中的贡献值了!因为菲涅尔方程直接给出了 kS, 我们可以使用 F 表示镜面反射在所有打在物体表面上的光线的贡献。 从kSkS我们很容易计算折射的比值 kD:

vec3 kS = F;
vec3 kD = vec3(1.0) - kS;

kD *= 1.0 - metallic; // metallic 参与
1
2
3
4
我们可以看作 kS 表示光能中被反射的能量的比例, 而剩下的光能会被折射, 比值即为 kD。更进一步来说,因为金属不会折射光线,因此不会有漫反射。所以如果表面是金属的,我们会把系数 kD 变为0。 这样,我们终于集齐所有变量来计算我们出射光线的值:

    float NdotL = max(dot(N, L), 0.0);        
    Lo += (kD * albedo / PI + specular) * radiance * NdotL; // albedo 参与
}
1
2
3
比较重要的是我们没有把 kS 乘进去我们的反射率方程中,这是因为我们已经在 specualr BRDF 中乘了菲涅尔系数F了,因为 kS 等于 F,因此我们不需要再乘一次。

TODO: 上面这句话不太理解。
这篇文章提到说是原公式是 不妥当 的 - ( 猴子都能看懂的PBR - https://zhuanlan.zhihu.com/p/33464301 )

从官方的源码上看到是没有乘上这个 ks 的

完整的直接光照PBR着色器
参考: 光照 - https://learnopengl-cn.github.io/07 PBR/02 Lighting/
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
in vec3 WorldPos;
in vec3 Normal;

// material parameters
uniform vec3  albedo;
uniform float metallic;
uniform float roughness;
uniform float ao;

// lights
uniform vec3 lightPositions[4];
uniform vec3 lightColors[4];

uniform vec3 camPos;

const float PI = 3.14159265359;

float DistributionGGX(vec3 N, vec3 H, float roughness);
float GeometrySchlickGGX(float NdotV, float roughness);
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness);
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness);

void main()
{       
    vec3 N = normalize(Normal);
    vec3 V = normalize(camPos - WorldPos);

    vec3 F0 = vec3(0.04); 
    F0 = mix(F0, albedo, metallic); // albedo 参与, metallic 参与, F0 参与

    // reflectance equation
    vec3 Lo = vec3(0.0);
    for(int i = 0; i < 4; ++i) 
    {
        // calculate per-light radiance
        vec3 L = normalize(lightPositions[i] - WorldPos);
        vec3 H = normalize(V + L);
        float distance    = length(lightPositions[i] - WorldPos);
        float attenuation = 1.0 / (distance * distance);
        vec3 radiance     = lightColors[i] * attenuation; // light color 参与     

        // cook-torrance brdf
        float NDF = DistributionGGX(N, H, roughness); // roughness 参与
        float G   = GeometrySmith(N, V, L, roughness); // roughness 参与
        vec3 F    = fresnelSchlick(max(dot(H, V), 0.0), F0);       

        vec3 kS = F;
        vec3 kD = vec3(1.0) - kS;
        kD *= 1.0 - metallic; // metallic 参与

        vec3 nominator    = NDF * G * F;
        float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001; // 分母项中加了一个0.001为了避免出现除零错误
        vec3 specular     = nominator / denominator;

        // add to outgoing radiance Lo
        float NdotL = max(dot(N, L), 0.0);                
        Lo += (kD * albedo / PI + specular) * radiance * NdotL; // albedo 参与
    }   

    vec3 ambient = vec3(0.03) * albedo * ao; // 环境光 term, ao 参与
    vec3 color = ambient + Lo;

    color = color / (color + vec3(1.0));
    color = pow(color, vec3(1.0/2.2));  //转到 gamma 空间

    FragColor = vec4(color, 1.0);
}  
————————————————
版权声明:本文为CSDN博主「君墨痕」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangxuan0261/article/details/88749969

这篇关于unity-shader-PBR基于物理渲染的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Matter.js:Web开发者的2D物理引擎

Matter.js:Web开发者的2D物理引擎 前言 在现代网页开发中,交互性和动态效果是提升用户体验的关键因素。 Matter.js,一个专为网页设计的2D物理引擎,为开发者提供了一种简单而强大的方式,来实现复杂的物理交互效果。 无论是模拟重力、碰撞还是复杂的物体运动,Matter.js 都能轻松应对。 本文将带你深入了解 Matter.js ,并提供实际的代码示例,让你一窥其强大功能

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

OpenGL ES 2.0渲染管线

http://codingnow.cn/opengles/1504.html Opengl es 2.0实现了可编程的图形管线,比起1.x的固定管线要复杂和灵活很多,由两部分规范组成:Opengl es 2.0 API规范和Opengl es着色语言规范。下图是Opengl es 2.0渲染管线,阴影部分是opengl es 2.0的可编程阶段。   1. 顶点着色器(Vert

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

JAVAEE初阶第七节(中)——物理原理与TCP_IP

系列文章目录 JAVAEE初阶第七节(中)——物理原理与TCP_IP 文章目录 系列文章目录JAVAEE初阶第七节(中)——物理原理与TCP_IP 一.应用层重点协议)1. DNS2 .NAT3. NAT IP转换过程 4 .NAPT5. NAT技术的缺陷6. HTTP/HTTPS7. 自定义协议 二. 传输层重点协议 1 .UDP协议 2.1.1 UDP协议端格式 2.1.2 UD

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj

在Unity环境中使用UTF-8编码

为什么要讨论这个问题         为了避免乱码和更好的跨平台         我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本,默认是看不到在VS中的编码格式,下面我介绍一种简单快