HLSL着色器原理:(一)着色器基础

  • 小光!小光!小光!小光!小光!
  • 本文所总结视频为或许是小光从油管搬运到B站的视频:传送门
  • 本篇主要汇总HLSL着色器的知识原理部分,并涉及少量必要的代码知识点,主要为知识点总结,实践部分建议参照其他Shader教程视频。
  • 本文主旨补充Unity客户端编程Shader知识点。
  • 我创建了一个游戏制作交流群:637959304 进群密码:(CSGO的拆包密码)欢迎各位大佬一起学习交流,不限于任何平台(U3D、UE、COCO2dx、GamesMaker等),以及欢迎编程,美术,音乐等游戏相关的任何人员一起进群学习交流。
  • 个人博客网址:HLSL着色器原理:(一)着色器基础 - Sugar的博客,如文章转载中出现例如传送门失效等纰漏,建议直接访问原博客网址。
  • 如有不对之处,欢迎指正。

目录

着色器基础

渲染管线

顶点着色器

像素着色器

着色器配置

简单着色器制作


着色器基础

渲染管线

  • HLSL编程内容:可编程顶点处理器顶点着色器程序,可编程像素处理器像素着色器程序。
  • 渲染管线工作流程
    软件运行
    ->软件向图形库(Direct3D、OpenGL等)发送指令
    ->图形库接受3D指令->图形库将软件CPU主进程运送到GPU图形进程
    ->图形卡将指令保存到GPU Front End,数据以顶点形式交移给显卡->继续转交顶点数据给可编程顶点处理程序
    ->处理程序将顶点坐标(物体系坐标)转换为屏幕坐标。【顶点着色器在此步骤可进行植入操作】
    ->顶点处理器将处理过后的顶点(即连接各个顶点)交给图元生成
    ->图元生成器根据数据生成对应的面,并将面交予光栅化/插值单元,面被切割成显示图像像素大小(将面转换成像素集,顶点上色彩数据也对应转化)
    ->差值数据送入可编程处理器;像素着色器在此获取该数据以及CPU中的其他数据
    ->像素着色器处理完成数据,决定最终像素的颜色
    ->数据转移到光栅处理,进入帧缓冲等待被显示器显示。

  • GPU Front End:专门设计用于GPU
  • 顶点着色器:1、可处理任何来自CPU发送给GPU的数据(移动顶点,改变顶点颜色,法线变换)2、准备数据给像素着色器。
  • 插值:利用法线计算,将不同顶点间的像素进行着色过渡处理(如下图所示)

  • 法线:在顶点着色器中,计算还是围绕顶点的,法线与顶点存在一起方便,法线的作用很多,其中之一是根据光线与法线夹角决定此顶点光照强度,这样一个三维物体你才能感受到他是三维的,如果一个球真的是纯白的,没有任何光的强度变化,看起来就是片了。

顶点着色器

  • 结构体:推荐使用结构体编辑数据,可以一次性编辑、传递、返回所有需要的数据。
  • 主要涉及数据:位置,贴图坐标,切线,法线,副法线
  • 什么是UV:HLSL着色器原理:(一)着色器基础 - Sugar的博客
  • 实现功能:
    1、根据输入的数据结构a2v,进行物体本地坐标到世界坐标的转换。包含法线,切线,副法线,
    2、通过矩阵计算得到法线,切线,副法线世界坐标
    3、计算光照向量以及观察向量
    4、将贴图纹理坐标转化为世界坐标
    5、计算顶点空间裁剪坐标(屏幕出现位置)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//数据

struct a2v

{

        float4 position : POSITION;//位置

        float2 texCoord :TEXCOORD0;//材质坐标

        float3 tangent : TANGENT;//切线

        float3 binormal : BINORMAL;//副法线

        float3 normal : NORMAL;//法线

}

struct v2f

{

//总共有10个寄存器,POSITION,TEXCOORD0-7,COLOR

        float4 position : POSITION;

//TEXCOORD0-5标注寄存器分配

        float2 texCoord : TEXCOORD0;

        float3 eyeVec :  TEXCOORD1;//观察者方向

        float3 lightVec : TEXCOORD2;//光照向量

//世界坐标下的法线等

        float3 worldNormal: TEXCOORD3;

        float3 worldTangent: TEXCOORD4;

        float3 worldBinormal: TEXCOORD5;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//mul:矩阵乘法

v2f v(a2v In,uniform float4 lightPosition

{

        v2f out;//顶点着色器输出结构体

        //矩阵运算,WorldInverseTranspose代表从物体空间转换到世界空间坐标。

        Out.worldNormal = mul(In.normal,WorldInverseTranspose).xyz;//In.normal 法线

        Out.WorldTangent = mul(In.tangent,WorldInverseTranspose).xyz;//切线

        Out.WorldBinormal = mul(In.Binormal,WorldInverseTranspose).xyz;//副法线

        float3 worldSpacePos = mul(In.position,World);//物体空间顶点转换到世界空间

       //漫反射光照和高光效果:

        Out.lightVec = lightPosition - worldSpacePos;//光照向量:世界坐标下光源坐标-顶点坐标

        Out.texCoord.xy = In.texCoord;//贴图,纹理坐标

        Out.eyeVec = ViewInverse[3].xyz - worldSpacePos;//观察向量:顶点到观察点向量,用来计算高光效果,同光照向量用世界坐标下观察坐标-顶点坐标

        Out.position = mul(In.position,WorldViewProjection);//计算顶点空间裁剪坐标,即屏幕上出现的位置

        return Out;//计算结果传递给像素着色器

}

  • 光照向量计算示意图

  • 观察向量示意图

像素着色器

  • 返回值为一个颜色,代表像素被渲染的结果
  • 实现功能:根据顶点着色器传入数据进行计算
    1、数据处理
    2、根绝光照向量,环境光,漫反射,高光,光泽度计算返回像素的颜色

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

float4 f(v2f In,uniform float4 lightColor) : COLOR//color绑定代表必须要将像素渲染到屏幕上

{

//fetch the diffuse and normal maps

        float4 ColorTexture = tex2D(diffuseMapSampler,In.texCoord.xy);//取出贴图上对应坐标的颜色。内置函数tex3D采样纹理贴图,In.texCoord.xy贴图坐标,即像素对应uv贴图坐标

        float3 normal = tex2D(normalMaoSampler,In.texCoord).xyz * 2.0 - 1.0;//同上进行法线的采样处理。*2.0-1.0因为颜色有RGB三个维度,取值范围[0,1],法线向量三维取值范围[-1,1],所以要把[0,1]值域映射到[-1,1]上。

//create tangent soace vectors,用于将采样得到的法向量从Tangent Space(切线空间)转换到世界空间。

        float3 Nn = In.worldNormal;//取出世界坐标下法线切线

        float3 Tn = In.worldTangent;//取出世界坐标下副法线方向

        float3 Bn = In.worldBinormal;

//offset world space normal with normal map values 法向量转到世界坐标系

        float3 N = (Nn * Normal.z) + (normal.x * Bn + normal.y * -Tn);//对Tn取负数因为有的法向量绿色通道的值是切向量的反方向。

        N = normalize(N);//内置函数-单位化,将N变为单位向量

//creat lighting vectors - view vector and light vector,光照向量,观察向量,法向量应长度一致

        float3 V = normalize(In.eyeVec);

        float3 L = normalize(In.lightVec.xyz);

//lighting

//ambient light环境光

        float4 C = AmbientColor * ColorTexture;//AmbientColor环境光颜色,来自于用户配置

        DiffuseColor *= ColorTexture;//漫反射光照

        SpecularColor *= ColorTexture.a;//高光 = 高光颜色乘以纹理颜色的不透明度,以实现高光遮罩效果    

//diffuse and specular light

        C += lightColor * blinn2(N,L,V,DiffuseColor,SpecularColor,Glossiness);//blinn2计算漫反射和高光效果,Glossiness光泽度

        return C;

}


着色器配置

  • 混合两种不同渲染方式的代码教程。//对应视频1.10内容
  • 可配置内容:
    1、不同版本、着色器图形硬件支持
    2、着色器类型、版本切换
  • 渲染阶段:每个着色器至少配置有一个渲染阶段,每一个阶段数据会走一遍渲染管线
  • 计算机根据传入深度函数来判断渲染前后关系

简单着色器制作

  • 对应视频章节1.11及其之后内容
  • 过滤模式:原始尺寸位图不加过滤会有锯齿,添加过滤器后(双线性,三种线性等)会改善图片质量。(图片放大倍数为100%,一个屏幕像素对应一个图片像素。但图像像素不是100%比例,屏幕像素中心可能落在图像像素任何位置。)
  • 双线性计算:采样周围四个像素数值,按照距离插值得到屏幕像素颜色以实现抗锯齿效果。
    如过图片没有正对摄像机,则需要各向异性的过滤器。如下图图三所示,x轴和y轴一格内像素数量不一致,所以y轴采样数量应大于x轴。但各向异性采样会大幅度提升采样次数,拖慢渲染速度。

之前

之后

非正对相机

  • 着色器实现:
    1、把坐标从物体空间转换到裁剪空间,传递UV坐标并采样漫反射贴图
    2、实现细节展示(细节贴图):当摄像机靠近物体渲染展示更多细节
    3、漫反射光照实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

float 4x4 mvp : WorldViewProjection<string UIWidget = "None";>; //用于将物体坐标系下坐标转换到屏幕(裁剪)坐标系下

struct app2vertex//输入结构体

{

        float4 position : POSITION;//获取顶点坐标;

        float2 texCoord : TEXCOORD0;//uv坐标

};

struct vertex2pixel//输出结构体

{

        float4 position : POSITION;//传递顶点坐标

        float2 texCoord : TEXCOORD0;//uv坐标

};

vertex2pixel vertex(app2vertex In)//顶点着色器

{

        vertex2pixel Out = (vertex2pixel)0;//将输出结构体清零

        Out.texCoord = In.texCoord;

        Out.position = mul(In.position,mvp);

        return Out;

};

float4 pixel(vertex2pixel In) : COLOR

{

        float4 col = float4(1.0,1.0,1.0,1.0);//设置颜色为白色并返回

        float4 ColorTexture = tex2D(diffuseMapSampler,In.textCoord);//tex2D为内置函数,用于采样贴图

        float4 DetailTexture = tex2D(detailMapSampler,In.textCoord);//下文细节贴图内容

        ColorTexture *= DetailTexture;

        return ColorTexture;

};

texture diffuseMap : DiffuseMap//为材质设置一个漫反射贴图

<

        string name = "default_color.dds";

        string UIName = "Diffuse Texture";

        string texturetype = "2D";//以2D形式解析贴图

>;

sampler2D diffuseMapSampler = sampler_state//采样器,位于GPU上,用于查找贴图对应位置颜色

{

        Texture = <diffuseMap>;//设置采样贴图

//过滤模式选择

        MinFilter = linear;

        MagFilter = linear;

        MipFilter = linear;

};

  • 细节贴图的数据结构(通过复制diffuseMap和diffuseMapSampler后进行改动)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

texture detailMap :

<

        string name = "default_color.dds";

        string UIName = "Detail Texture Texture";

        string texturetype = "2D";

>;

sampler2D detailSampler = sampler_state

{

        Texture = <detialMap>;

        MinFilter = linear;

        MagFilter = linear;

        MipFilter = linear;

};

//用户自定义平铺次数

float tile

<

        string UIWidget = "spinner";//步进器控件,用于修改数值

        int UIMin = 1;//代表数据最小值

        int UIMax = 100;//最大值

        int UIStep = 1;//步进步距

        string UIName = "Detail Tile";

> = 8;//默认重复平铺的次数

  • 漫反射光照原理:漫反射光照模型要求,当光源或者物体移动时,光照信息随两者位置和方向不同而更新。
    1、首先得到表面法向,以及光照向量,(表示物体表面与光源的方向,即光照向量=光原坐标-表面坐标)
    2、根据表面法相以及光照向量计算表面受光程度(用点积计算)。(如下图所示)
    3、漫反射=点积结果x表面颜色

  • 漫反射数据结构

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

float4x4 world : World<string UIWidget = "None";>;//世界坐标系变换矩阵

//获取光照位置,光照颜色

float4 lightPos : POSITION

<

        string UIName = "Light Position";

        string Object = "PointLight";

        int refID = 0;

> = (100.0f, 100.0f, 100.0f, 100.0f);

float4 lightColor : LIGHTCOLOR

<

        int LightRef = 0;

> = {1.0f,1.0f,1.0f,0.0f};

vertex2pixel vertex(app2vertex In)

{

        vertex2pixel Out = {vertex2pixel)0;

        Our.position = mul(In.position,mvp);

        float3 worldSpacePos = mul(In.position,world);//把顶点坐标转换到世界空间,向量必须在同坐标系才能进行计算

        float3 lightVec = lightPos - worldSpacePos;//计算光照向量

        float3 L = normalize(lightVec);//单位化向量,以进行点积计算

//需要先在app2vertex中定义normal法线

        float3 N = In.normal;

        float brightness = dot(N,L);//点积计算。如果角度超过90度,点积值为负数

        float brightnessClamped = max(brightness,0);

//在vertex2pixel中定义diffuse

        Out.diffuse = brightnessClamped * lightColor;

        return Out;

}

float4 pixel(vertex2pixel In) : COLOR

{

        float4 col = In.diffuse;

        return col;

}

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以提供基于HLSL的PBR着色器的示例代码: // HLSL Shader // PBR Shader float4 PS_PBR(VertexShaderOutput input) : SV_Target { // Albedo Color float3 albedo = tex2D(albedoMap, input.uv).rgb; // Normal Map float3 normal = tex2D(normalMap, input.uv).rgb * 2.0 - 1.0; // Metallic Map float metallic = tex2D(metallicMap, input.uv).r; // Roughness Map float roughness = tex2D(roughnessMap, input.uv).r; // Ambient Occlusion Map float ao = tex2D(aoMap, input.uv).r; // Convert normal map to world space normal = mul(normal, (float3x3)input.tangentToWorld); // Calculate PBR float4 pbr = CalculatePBR(albedo, normal, metallic, roughness, ao); return pbr; } ### 回答2: PBR(PBR, Physically Based Rendering)是一种基于物理原理的渲染技术,可以实现更加逼真的光照效果。在编写PBR着色器时,我们可以使用HLSL(高级着色语言)来描述着色器的计算逻辑。 首先,我们需要定义一些常量和变量,用来存储PBR着色器中的参数和数据。常见的PBR参数包括:环境光照强度、漫反射光照强度、粗糙度、金属度等。在HLSL中,我们可以使用常量缓冲区来定义这些参数。 接下来,我们需要在像素着色器中进行PBR计算。像素着色器可以接收几个输入参数,包括:法线向量、视线向量和光线向量。我们可以使用这些向量来计算当前像素的PBR光照结果。 在进行计算之前,我们需要根据法线贴图获得每个像素的法线向量。接着,我们可以计算出来自每个光源的直接光照贡献。 PBR光照模型通常包含两个部分:漫反射和镜面反射。对于漫反射,我们可以使用标准的Lambertian光照模型。对于镜面反射,我们可以使用Cook-Torrance BRDF模型。 在计算完直接光照贡献后,我们可以结合环境光照来计算最终的颜色值。在这个过程中,我们使用漫反射和镜面反射的结果加权求和。 最后,根据计算出的颜色值,我们可以在输出的像素中返回最终的颜色。这样,我们就完成了一个基本的PBR着色器。 需要注意的是,以上只是PBR着色器的基本框架,在实际应用中,我们还需要添加其他优化和特性,如预过滤环境贴图、BRDF近似等。 总结起来,通过使用HLSL编写PBR着色器,我们可以实现基于物理原理的光照渲染效果。这种着色器由漫反射和镜面反射组成,并利用各种参数和计算公式来模拟光照对物体的影响,从而实现更加逼真的渲染效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值