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

  3.2 游戏引擎技术
  • 小光!小光!小光!小光!小光!
  • 本文所总结视频为或许是小光从油管搬运到B站的视频:传送门
  • 本篇主要汇总HLSL着色器的知识原理部分,并涉及少量必要的代码知识点,主要为知识点总结,实践部分建议参照其他Shader教程视频。
  • 本文主旨补充Unity客户端编程Shader知识点。
  • 我创建了一个游戏制作交流群:637959304 进群密码:(CSGO的拆包密码)欢迎各位大佬一起学习交流,不限于任何平台(U3D、UE、COCO2dx、GamesMaker等),以及欢迎编程,美术,音乐等游戏相关的任何人员一起进群学习交流。

着色器基础

渲染管线

  • HLSL编程内容:可编程顶点处理器顶点着色器程序,可编程像素处理器像素着色器程序。
  • 渲染管线工作流程
    软件运行
    ->软件向图形库(Direct3D、OpenGL等)发送指令
    ->图形库接受3D指令->图形库将软件CPU主进程运送到GPU图形进程
    ->图形卡将指令保存到GPU Front End,数据以顶点形式交移给显卡->继续转交顶点数据给可编程顶点处理程序
    ->处理程序将顶点坐标(物体系坐标)转换为屏幕坐标。【顶点着色器在此步骤可进行植入操作】
    ->顶点处理器将处理过后的顶点(即连接各个顶点)交给图元生成
    ->图元生成器根据数据生成对应的面,并将面交予光栅化/插值单元,面被切割成显示图像像素大小(将面转换成像素集,顶点上色彩数据也对应转化)
    ->差值数据送入可编程处理器;像素着色器在此获取该数据以及CPU中的其他数据
    ->像素着色器处理完成数据,决定最终像素的颜色
    ->数据转移到光栅处理,进入帧缓冲等待被显示器显示。
image 38 1024x817 - HLSL着色器原理:(一)着色器基础
  • GPU Front End:专门设计用于GPU
  • 顶点着色器:1、可处理任何来自CPU发送给GPU的数据(移动顶点,改变顶点颜色,法线变换)2、准备数据给像素着色器。
  • 插值:利用法线计算,将不同顶点间的像素进行着色过渡处理(如下图所示)
image 37 - HLSL着色器原理:(一)着色器基础
  • 法线:在顶点着色器中,计算还是围绕顶点的,法线与顶点存在一起方便,法线的作用很多,其中之一是根据光线与法线夹角决定此顶点光照强度,这样一个三维物体你才能感受到他是三维的,如果一个球真的是纯白的,没有任何光的强度变化,看起来就是片了。

顶点着色器

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

像素着色器

  • 返回值为一个颜色,代表像素被渲染的结果
  • 实现功能:根据顶点着色器传入数据进行计算
    1、数据处理
    2、根绝光照向量,环境光,漫反射,高光,光泽度计算返回像素的颜色
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轴。但各向异性采样会大幅度提升采样次数,拖慢渲染速度。
image 42 - HLSL着色器原理:(一)着色器基础
之前
image 43 - HLSL着色器原理:(一)着色器基础
之后
image 44 - HLSL着色器原理:(一)着色器基础
非正对相机
  • 着色器实现:
    1、把坐标从物体空间转换到裁剪空间,传递UV坐标并采样漫反射贴图
    2、实现细节展示(细节贴图):当摄像机靠近物体渲染展示更多细节

    3、漫反射光照实现
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后进行改动)
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表面颜色
image 45 - HLSL着色器原理:(一)着色器基础
  • 漫反射数据结构
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;
}

LEAVE A COMMENT