DirectX11 With Windows SDK--25 法线贴图

前言

在很早之前的纹理映射中,纹理存放的元素是像素的颜色,通过纹理坐标映射到目标像素以获取其颜色。但是我们的法向量依然只是定义在顶点上,对于三角形面内一点的法向量,也只是通过比较简单的插值法计算出相应的法向量值。这对平整的表面比较有用,但无法表现出内部粗糙的表面。在这一章,你将了解如何获取更高精度的法向量以描述一个粗糙平面。

DirectX11 With Windows SDK完整目录

Github项目源码

法线贴图

法线贴图是指纹理中实际存放的元素通常是经过压缩后的法向量,用于表现一个表面凹凸不平的特性,它是凹凸贴图的一种实现方式。

开启法线贴图后的效果

关闭法线贴图后的效果

法线贴图中存放的法向量\((x, y, z)\)分别对应原来的\((r, g, b)\)。每个像素都存放了对应的一个法向量,经过压缩后使用24 bit即可表示。实际情况则是一张法线贴图里面的每个像素使用了32 bit来表示,剩余的8 bit(位于Alpha值)要么可以不使用,要么用来表示高度值或者镜面系数。而未经压缩的法线贴图通常为每个像素存放4个浮点数,即使用128 bit来表示。

下面展示了一张法线贴图,每个像素点位置存放了任意方向的法向量。可以看到这里为法线贴图建立了一个TBN坐标系(左手坐标系),其中T轴(Tangent Axis)对应原来的x轴,B轴(Binormal Axis)对应原来的y轴,N轴(Normal Axis)对应原来的z轴。建立坐标系的目的在后面再详细描述。观察这些法向量,它们都有一个共同的特点,就是都朝着N轴的正方向散射,这样使得大多数法向量的z分量是最大的。

由于压缩后的法线贴图通常是以R8G8B8A8的格式存储,我们也可以直接把它当做图片来打开观察。

前面说到大部分法向量的z分量会比x, y分量大,导致整个图看起来会偏蓝。

法线贴图的压缩与解压

经过初步压缩后的法线贴图的占用空间为原来的1/4(不考虑文件头),就算每个分量只有256种表示,也足够表示出16777216种不同的法向量了。假如现在我们已经有未经过压缩的法线贴图,那要怎么进行初步压缩呢?

对于一个单位法向量来说,其任意一个分量的取值也无非就是落在[-1, 1]的区间上。现在我们要将其映射到[0, 255]的区间上,可以用下面的公式来进行压缩:

\[f(x) = (0.5x + 0.5) * 255\]

而如果现在拿到的是24位法向量,要进行还原,则可以用下面的公式:

\[ f^-1(x) = \frac{2x}{255} - 1\]

当然,经过还原后的法向量是有部分的精度损失了,至少能够映射回[-1, 1]的区间上。

通常情况下我们能拿到的都是经过压缩后的法线贴图,但是还原工作还是需要由自己来完成。

float3 normalT = gNormalMap.Sample(sam, pin.Tex);

经过上面的采样后,normalT的每个分量会自动从[0, 255]映射到[0, 1],但还不是最终[-1, 1]的区间。因此我们还需要完成下面这一步:

normalT = 2.0f * normalT - 1.0f;

这里的1.0f会扩展成float3(1.0f, 1.0f, 1.0f)以完成减法运算。

注意:如果你想要使用压缩纹理格式(对原来的R8G8B8A8进一步压缩)来存储法线贴图,可以使用BC7(DXGI_FORMAT_BC7_UNORM)来获得最佳性能。在DirectXTex中有大量从BC1到BC7的纹理压缩/解压函数。

纹理/切线空间

这里开始就会产生一个疑问了,为什么需要切线空间?

在只有2D的纹理坐标系仅包含了U轴和V轴,但现在我们的纹理中存放的是法向量,这些法向量要怎么变换到局部物体上某一个三角形对应位置呢?这就需要我们对当前法向量做一次矩阵变换(平移和旋转),使它能够来到局部坐标系下物体的某处表面。由于矩阵变换涉及到的是坐标系变换,我们需要先在原来的2D纹理坐标系加一条坐标轴(N轴),与T轴(原来的U轴)和B轴(原来的V轴)相互垂直,以此形成切线空间。

一开始法向量处在单位切线空间,而需要变换到目标3D三角形的位置也有一个对应的切线空间。对于一个立方体来说,一个面的两个三角形可以共用一个切线空间。

利用顶点位置和纹理坐标求TBN坐标系

现在假设我们的顶点只包含了位置和纹理坐标这两个信息,有这样一个三角形,它们的顶点为V0(x0, y0, z0), V1(x1, y1, z1), V2(x2, y2, z2),纹理坐标为(u0, v0), (u1, v1), (u2, v2)。

图片展示了一个三角形与所处的切线空间,我们可以这样定义向量E0E1

\[\mathbf{e_0} = \mathbf{V_1} - \mathbf{V_0}\]
\[\mathbf{e_1} = \mathbf{V_2} - \mathbf{V_0}\]

现在T轴和B轴都是待求的单位向量,可以列出下述关系:

\[(\Delta u_0, \Delta v_0) = (u_1 - u_0, v_1 - v_0)\]
\[(\Delta u_1, \Delta v_1) = (u_2 - u_0, v_2 - v_0)\]
\[\mathbf{e_0} = \Delta u_0\mathbf{T} + \Delta v_0\mathbf{B}\]
\[\mathbf{e_1} = \Delta u_1\mathbf{T} + \Delta v_1\mathbf{B}\]

把它用矩阵来描述:

\[ \begin{bmatrix} \mathbf{e_0} \\ \mathbf{e_1} \end{bmatrix} =
\begin{bmatrix} \Delta u_0 & \Delta v_0 \\ \Delta u_1 & \Delta v_1 \end{bmatrix}
\begin{bmatrix} \mathbf{T} \\ \mathbf{B} \end{bmatrix} \]

继续细化:

\[ \begin{bmatrix} e_{0x} & e_{0y} & e_{0z} \\ e_{1x} & e_{1y} & e_{1z} \end{bmatrix} =
\begin{bmatrix} \Delta u_0 & \Delta v_0 \\ \Delta u_1 & \Delta v_1 \end{bmatrix}
\begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} \]

为了计算TB矩阵,需要在等式两边左乘uv矩阵的逆:

\[ {\begin{bmatrix} \Delta u_0 & \Delta v_0 \\ \Delta u_1 & \Delta v_1 \end{bmatrix}}^{-1}
\begin{bmatrix} e_{0x} & e_{0y} & e_{0z} \\ e_{1x} & e_{1y} & e_{1z} \end{bmatrix} =
\begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} \]

对于一个二阶矩阵顶点求逆,我们不考虑过程。已知有矩阵\(\mathbf{A} = \begin{bmatrix} a & b \\ c & d \end{bmatrix}\),那么它的逆矩阵为:

\[ \mathbf{A}^{-1} = \frac{1}{ad-bc}\begin{bmatrix} d & -b \\ -c & a \end{bmatrix} \]

因此上面的方程最终变成:

\[ \begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} =
\frac{1}{\Delta u_0 \Delta v_1 - \Delta v_0 \Delta u_1}
\begin{bmatrix} \Delta v_1 & - \Delta v_0 \\ -\Delta u_1 & \Delta u_0 \end{bmatrix}
\begin{bmatrix} e_{0x} & e_{0y} & e_{0z} \\ e_{1x} & e_{1y} & e_{1z} \end{bmatrix} \]

这里可以找一个例子尝试一下:
V0坐标为(0, 0, -0.25), 纹理坐标为(0, 0.5)
V1坐标为(0.15, 0, 0), 纹理坐标为(0.3, 0)
V2坐标为(0.4, 0, 0), 纹理坐标为(0.8, 0)

求解过程如下:
\[ \mathbf{e_0} = \mathbf{V_1} - \mathbf{V_0} = (0.15, 0, 0.25) \]
\[ \mathbf{e_1} = \mathbf{V_2} - \mathbf{V_0} = (0.4, 0, 0.25) \]
\[ (\Delta u_0, \Delta v_0) = (u_1 - u_0, v_1 - v_0) = (0.3, -0.5) \]
\[ (\Delta u_1, \Delta v_1) = (u_2 - u_0, v_2 - v_0) = (0.8, -0.5) \]
\[ \begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} =
\frac{1}{0.3 \times (-0.5) - (-0.5) \times 0.8}
\begin{bmatrix} -0.5 & 0.5 \\ -0.8 & 0.3 \end{bmatrix}
\begin{bmatrix} 0.15 & 0 & 0.25 \\ 0.4 & 0 & 0.25 \end{bmatrix} =
\begin{bmatrix} 0.5 & 0 & 0 \\ 0 & 0 & -0.5 \end{bmatrix}
\]

由于位置坐标和纹理坐标的不一致性,导致求出来的T向量和B向量很有可能不是单位向量。仅当位置坐标的变化率与纹理坐标的变化率相同时才会得到单位向量。这里我们将其进行标准化即可。

但如果对纹理坐标进行了变换,有可能导致T轴和B轴不相互垂直。比如尝试用球体网格模型某个三角形面内的一点映射到球面上一点。

顶点切线空间

上面的运算得到的切线空间是基于单个三角形的,可以看到其运算过程还是比较复杂,而且交给着色器来进行运算的话还会产生大量的指令。

我们可以为顶点添加法向量N和切线向量T用于构建基于顶点的切线空间。很早之前提到法向量是与该顶点共用的所有三角形的法向量取平均值所得到的。切线向量也一样,它是与该顶点共用的所有三角形的切线向量取平均值所得到的。

现在Vertex.h定义了我们的新顶点类型:

struct VertexPosNormalTangentTex
{
    DirectX::XMFLOAT3 pos;
    DirectX::XMFLOAT3 normal;
    DirectX::XMFLOAT4 tangent;
    DirectX::XMFLOAT2 tex;
    static const D3D11_INPUT_ELEMENT_DESC inputLayout[4];
};

这里的tangent是一个4D向量,考虑到要和微软DXTK定义的顶点类型保持一致,多出来的w分量可以留作他用,这里暂不讨论。

施密特向量正交化

通常顶点提供的NT通常是相互垂直的,并且都是单位向量,我们可以通过计算\(\mathbf{B} = \mathbf{N} \times \mathbf{T}\)来得到副法线向量B,使得顶点可以不需要存放副法线向量B。但是经过插值计算后的NT可能会导致不是相互垂直,我们最好还是要通过施密特正交化来获得实际的切线空间。

现在已知互不垂直的N向量和T向量,我们希望求出与N向量垂直的T‘向量,需要将T向量投影到N向量上。

从上面的图我们可以知道最终求得的T‘

\[ \mathbf{T'} = \lVert \mathbf{T} - (\mathbf{T} \cdot \mathbf{N}) \mathbf{N} \rVert \]

B‘ 最终也可以确定下来
\[ \mathbf{B'} = \mathbf{N} \times \mathbf{T'}\]

这样T‘, B‘, N相互垂直,可以构成TBN坐标系。在后面的着色器实现中我们也会用到这部分内容。

切线空间的变换

一开始的切线空间可以用一个单位矩阵来表示,切线向量正是处在这个空间中。紧接着就是需要对其进行一次到局部对象(具体到某个三角形)切线空间的变换:

\[ \mathbf{M}_{object} = \begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \\ N_x & N_y & N_z \end{bmatrix} \]

然后切线向量随同世界矩阵一同进行变换来到世界坐标系,因此我们可以把它写成:

\[ \mathbf{n}_{world} = \mathbf{n}_{tangent}\mathbf{M}_{object}\mathbf{M}_{world} \]

注意:

  1. 对切线向量进行矩阵变换,我们只需要使用3x3的矩阵即可。
  2. 法线向量变换到世界矩阵需要用世界矩阵求逆的转置进行校正,而对切线向量只需要用世界矩阵变换即可。下图演示了将宽度拉伸为原来2倍后,法线和切线向量的变化:

HLSL代码

为了使用法线贴图,我们需要完成下列步骤:

  1. 获取该纹理所需要用到的法线贴图,在C++端为其创建一个ID3D11Texture2D。这里不考虑如何制作一张法线贴图。
  2. 对于一个网格模型来说,顶点数据需要包含位置、法向量、切线向量、纹理坐标四个元素。同样这里不讨论模型的制作,在本教程使用的是Geometry所生成的网格模型
  3. 在顶点着色器中,将顶点法向量和切线向量从局部坐标系变换到世界坐标系
  4. 在像素着色器中,使用经过插值的法向量和切线向量来为每个三角形表面的像素点构建TBN坐标系,然后将切线空间的法向量变换到世界坐标系中,这样最终求得的法向量用于光照计算。

现在我们的Basic.hlsli沿用的是第23章动态天空盒的部分,变化如下:

Texture2D gDiffuseMap : register(t0);
Texture2D gNormalMap : register(t1);
TextureCube gTexCube : register(t2);
SamplerState gSam : register(s0);

// 使用的是第23章的常量缓冲区,省略...
// 省略和之前一样的结构体...

struct VertexPosNormalTangentTex
{
    float3 PosL : POSITION;
    float3 NormalL : NORMAL;
    float4 TangentL : TANGENT;
    float2 Tex : TEXCOORD;
};

struct InstancePosNormalTangentTex
{
    float3 PosL : POSITION;
    float3 NormalL : NORMAL;
    float4 TangentL : TANGENT;
    float2 Tex : TEXCOORD;
    matrix World : World;
    matrix WorldInvTranspose : WorldInvTranspose;
};

struct VertexPosHWNormalTangentTex
{
    float4 PosH : SV_POSITION;
    float3 PosW : POSITION; // 在世界中的位置
    float3 NormalW : NORMAL; // 法向量在世界中的方向
    float4 TangentW : TANGENT; // 切线在世界中的方向
    float2 Tex : TEXCOORD;
};

float3 NormalSampleToWorldSpace(float3 normalMapSample,
    float3 unitNormalW,
    float4 tangentW)
{
    // 将读取到法向量中的每个分量从[0, 1]还原到[-1, 1]
    float3 normalT = 2.0f * normalMapSample - 1.0f;

    // 构建位于世界坐标系的切线空间
    float3 N = unitNormalW;
    float3 T = normalize(tangentW.xyz - dot(tangentW.xyz, N) * N); // 施密特正交化
    float3 B = cross(N, T);

    float3x3 TBN = float3x3(T, B, N);

    // 将凹凸法向量从切线空间变换到世界坐标系
    float3 bumpedNormalW = mul(normalT, TBN);

    return bumpedNormalW;
}

上面的NormalSampleToWorldSpace函数用于将法向量从切线空间变换到世界空间,位于Basic.hlsli。它接受了3个参数:从法线贴图采样得到的向量,变换到世界坐标系的法向量和切线向量。

然后是顶点着色器:

// NormalMapObject_VS.hlsl
#include "Basic.hlsli"

// 顶点着色器
VertexPosHWNormalTangentTex VS(VertexPosNormalTangentTex vIn)
{
    VertexPosHWNormalTangentTex vOut;

    matrix viewProj = mul(gView, gProj);
    vector posW = mul(float4(vIn.PosL, 1.0f), gWorld);

    vOut.PosW = posW.xyz;
    vOut.PosH = mul(posW, viewProj);
    vOut.NormalW = mul(vIn.NormalL, (float3x3) gWorldInvTranspose);
    vOut.TangentW = mul(vIn.TangentL, gWorld);
    vOut.Tex = vIn.Tex;
    return vOut;
}
// NormalMapInstance_VS.hlsl
#include "Basic.hlsli"

// 顶点着色器
VertexPosHWNormalTangentTex VS(InstancePosNormalTangentTex vIn)
{
    VertexPosHWNormalTangentTex vOut;

    matrix viewProj = mul(gView, gProj);
    vector posW = mul(float4(vIn.PosL, 1.0f), vIn.World);

    vOut.PosW = posW.xyz;
    vOut.PosH = mul(posW, viewProj);
    vOut.NormalW = mul(vIn.NormalL, (float3x3) vIn.WorldInvTranspose);
    vOut.TangentW = mul(vIn.TangentL, vIn.World);
    vOut.Tex = vIn.Tex;
    return vOut;
}

相比之前的像素着色器,现在它多了对法线映射的处理:

// 法线映射
float3 normalMapSample = gNormalMap.Sample(gSam, pIn.Tex).rgb;
float3 bumpedNormalW = NormalSampleToWorldSpace(normalMapSample, pIn.NormalW, pIn.TangentW);

求得的法向量bumpedNormalW将用于光照计算。

现在完整的像素着色器代码如下:

// NormalMap_PS.hlsl
#include "Basic.hlsli"

// 像素着色器(3D)
float4 PS(VertexPosHWNormalTangentTex pIn) : SV_Target
{
    // 若不使用纹理,则使用默认白色
    float4 texColor = float4(1.0f, 1.0f, 1.0f, 1.0f);

    if (gTextureUsed)
    {
        texColor = gDiffuseMap.Sample(gSam, pIn.Tex);
        // 提前进行裁剪,对不符合要求的像素可以避免后续运算
        clip(texColor.a - 0.1f);
    }

    // 标准化法向量
    pIn.NormalW = normalize(pIn.NormalW);

    // 求出顶点指向眼睛的向量,以及顶点与眼睛的距离
    float3 toEyeW = normalize(gEyePosW - pIn.PosW);
    float distToEye = distance(gEyePosW, pIn.PosW);

    // 法线映射
    float3 normalMapSample = gNormalMap.Sample(gSam, pIn.Tex).rgb;
    float3 bumpedNormalW = NormalSampleToWorldSpace(normalMapSample, pIn.NormalW, pIn.TangentW);

    // 初始化为0
    float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 A = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 D = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 S = float4(0.0f, 0.0f, 0.0f, 0.0f);
    int i;

    [unroll]
    for (i = 0; i < 5; ++i)
    {
        ComputeDirectionalLight(gMaterial, gDirLight[i], bumpedNormalW, toEyeW, A, D, S);
        ambient += A;
        diffuse += D;
        spec += S;
    }

    [unroll]
    for (i = 0; i < 5; ++i)
    {
        ComputePointLight(gMaterial, gPointLight[i], pIn.PosW, bumpedNormalW, toEyeW, A, D, S);
        ambient += A;
        diffuse += D;
        spec += S;
    }

    [unroll]
    for (i = 0; i < 5; ++i)
    {
        ComputeSpotLight(gMaterial, gSpotLight[i], pIn.PosW, bumpedNormalW, toEyeW, A, D, S);
        ambient += A;
        diffuse += D;
        spec += S;
    }

    float4 litColor = texColor * (ambient + diffuse) + spec;

    // 反射
    if (gReflectionEnabled)
    {
        float3 incident = -toEyeW;
        float3 reflectionVector = reflect(incident, pIn.NormalW);
        float4 reflectionColor = gTexCube.Sample(gSam, reflectionVector);

        litColor += gMaterial.Reflect * reflectionColor;
    }
    // 折射
    if (gRefractionEnabled)
    {
        float3 incident = -toEyeW;
        float3 refractionVector = refract(incident, pIn.NormalW, gEta);
        float4 refractionColor = gTexCube.Sample(gSam, refractionVector);

        litColor += gMaterial.Reflect * refractionColor;
    }

    litColor.a = texColor.a * gMaterial.Diffuse.a;
    return litColor;
}

所有的着色器将共用Basic.hlsli。而对BasicEffect的变化(和C++的交互)这里我们不讨论。

下面的动画演示了法线贴图的对比效果(GIF画质有点渣):

至此进阶篇就告一段落了。

DirectX11 With Windows SDK完整目录

Github项目源码

原文地址:https://www.cnblogs.com/X-Jun/p/10217421.html

时间: 2024-08-29 09:34:05

DirectX11 With Windows SDK--25 法线贴图的相关文章

DirectX11 With Windows SDK 24--Render-To-Texture(RTT)技术的应用、使用ScreenGrab保存纹理到文件

前言 尽管在上一章的动态天空盒中用到了Render-To-Texture技术,但那是针对纹理立方体的特化实现.考虑到该技术的应用层面非常广,在这里抽出独立的一章专门来讲有关它的通用实现以及各种应用.此外,这里还会讲到如何使用DirectXTex的ScreenGrab来保存纹理,可以说是干货满满了. 如果想要看Render-To-Texture在动态天空盒的应用,可以点此回顾: 章节 23 立方体映射:动态天空盒的实现 DirectX11 With Windows SDK完整目录 Github项目

粒子系统与雨的效果 (DirectX11 with Windows SDK)

前言 最近在学粒子系统,看这之前的<<3D图形编程基础 基于DirectX 11 >>是基于Direct SDK的,而DXSDK微软已经很久没有更新过了并且我学的DX11是用Windows SDK来实现. 顺手安利一波:我最近在学DX11 with WindowSDK 教程 博客地址: https://www.cnblogs.com/X-Jun/p/9028764.html 所以下面的都是基于这个教程和上面提到的那本书来写的,推荐看到教程17章和书里第15章之后再来看这篇博客,有助

DirectX11 With Windows SDK--15 几何着色器初探

# 前言 从这一部分开始,感觉就像是踏入了无人深空一样,在之前初学DX11的时候,这部分内容都是基本上跳过的,现在打算重新认真地把它给拾回来. [DirectX11 With Windows SDK完整目录](http://www.cnblogs.com/X-Jun/p/9028764.html) [Github项目源码](https://github.com/MKXJun/DirectX11-With-Windows-SDK) # 几何着色器 首先用一张图来回顾一下渲染管线的各个阶段,目前为止

DirectX11 With Windows SDK--16 利用几何着色器可选的流输出阶段帮助绘制多种分形

前言 在上一章,我们知道了如何使用几何着色器来重新组装图元,比如从一个三角形分裂成三个三角形.但是为了实现更高阶的分形,我们必须要从几何着色器拿到输出的顶点.这里我们可以使用可选的流输出阶段来拿到顶点集合. 注意: 本章末尾有大量的GIF动图! DirectX11 With Windows SDK完整目录 Github项目源码 流输出阶段 现在我们知道GPU可以写入纹理(textures),例如深度/模板缓冲区以及后备缓冲区.当然,我们也可以通过渲染管线的流输出阶段让GPU将几何着色器输出的顶点

DirectX11 With Windows SDK--17 利用几何着色器实现公告板效果

前言 上一章我们知道了如何使用几何着色器将顶点通过流输出阶段输出到绑定的顶点缓冲区.接下来我们继续利用它来实现一些新的效果,在这一章,你将了解: 实现公告板效果 Alpha-To-Coverage 对GPU资源进行读/写操作 纹理数组 实现雾效 DirectX11 With Windows SDK完整目录 Github项目源码 实现雾效 虽然这部分与几何着色器并没有什么关系,但是雾的效果在该Demo中会用到,并且前面也没有讲过这部分内容,故先在这里提出来. 有时候我们需要在游戏中模拟一些特定的天

DirectX11 With Windows SDK--23 立方体映射:动态天空盒的实现

前言 上一章的静态天空盒已经可以满足绝大部分日常使用了.但对于自带反射/折射属性的物体来说,它需要依赖天空盒进行绘制,但静态天空盒并不会记录周边的物体,更不用说正在其周围运动的物体了.因此我们需要在运行期间构建动态天空盒,将周边物体绘制入当前的动态天空盒. 没了解过静态天空盒的读者请先移步到下面的链接: 章节回顾 22 立方体映射:静态天空盒的读取与实现 DirectX11 With Windows SDK完整目录 Github项目源码 欢迎加入QQ群: 727623616 可以一起探讨DX11

DirectX11 With Windows SDK--29 计算着色器:内存模型、线程同步;实现顺序无关透明度(OIT)

前言 由于透明混合在不同的绘制顺序下结果会不同,这就要求绘制前要对物体进行排序,然后再从后往前渲染.但即便是仅渲染一个物体(如上一章的水波),也会出现透明绘制顺序不对的情况,普通的绘制是无法避免的.如果要追求正确的效果,就需要对每个像素位置对所有的像素按深度值进行排序.本章将介绍一种仅DirectX11及更高版本才能实现的顺序无关的透明度(Order-Independent Transparency,OIT),虽然它是用像素着色器来实现的,但是用到了计算着色器里面的一些相关知识. 这一章综合性很

DirectX11 With Windows SDK--30 计算着色器:图像模糊、索贝尔算子

前言 到这里计算着色器的主线学习基本结束,剩下的就是再补充两个有关图像处理方面的应用.这里面包含了龙书11的图像模糊,以及龙书12额外提到的Sobel算子进行边缘检测.主要内容源自于龙书12,项目源码也基于此进行调整. 学习目标: 熟悉图像处理常用的卷积 熟悉高斯模糊.Sobel算子 DirectX11 With Windows SDK完整目录 Github项目源码 欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报. 图像卷积 在图像处理中,经常需要用到

DirectX11 With Windows SDK--11 混合状态与光栅化状态

前言 DirectX11 With Windows SDK完整目录:http://www.cnblogs.com/X-Jun/p/9028764.html 虽然这一部分的内容主要偏向于混合(Blending),但这里还需提及一下,关于渲染管线可以绑定的状态主要有如下三种: 光栅化状态(光栅化阶段) 混合状态(输出合并阶段) 深度/模板状态(输出合并阶段) Direct3D是基于状态机的,我们可以通过修改这些状态来修改渲染管线的当前行为. 实际上这一章会讲述光栅化状态和混合状态这两个部分,在后续的