仅供个人学习使用,请勿转载。谢谢!
本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合。因此,该技术可以用来渲染如水和玻璃之类的半透物体。
源alpha分量可以用于在RGB混合的过程中控制像素的透明度,而混合方程中的源颜色实际上来自于像素着色器。在第九章中,我们将漫反射材质中的alpha分量作为纹理着色器的alpha分量输出,因此,我们可以利用漫反射图(diffuse map)中的alpha通道来控制混合过程中的透明度了。
float4 PS(VertexOut pin) : SV_Target
{
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap,pin.TexC)*gDiffuseAlbedo;
……
//从漫发射反照率获取alpha值的常用方法
litColor.a = diffuseAlbedo.a;
return litColor;
}
我们可以在常见的图像编辑软件中添加alpha通道,然后到处为支持alpha通道的格式,如DDS
有些时候,我们希望彻底禁止某一个像素参与后续的处理,这时候我们可以通过HLSL中的内置函数clip(x)实现,该函数仅供像素着色器使用,当x < 0时,该像素将会不在参与后面的处理阶段,因此用这个函数来绘制透明和非透明相见的像素是最合适的了。(比如铁丝网)
在像素着色器中,我们将会采集像素的alpha分量,如果该值极小接近于0,则表明该像素是完全透明的,那么我们将会通过clip(x)函数将该像素彻底从后续的处理阶段中移除。
float4 PS(VertexOut pin) : SV_Traget
{
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap,pin.TexC)*gDiffuseAlbedo;
#ifdef ALPHA_TEXT
//如果alpha < 0.1f,则抛弃该像素,我们要在着色器中尽早执行该操作,以尽快检测处满足条件的像素并
//退出着色器,从而跳过后续的处理过程
clip(diffuseAlbedo.a - 0.1f);
#endif
……
//从漫发射反照率中获取alpha值的常用手段
litColor.a = diffuseAlbedo.a;
return litColor;
}
通过上述的代码可以看出,只有在定义了ALPHA_TEXT宏的时候我们才会执行透明像素的筛选。因为我们有时候可以不希望对某些渲染项执行clip(x)方法,所以我们要有能力针对特殊的着色器开启或关闭clip函数的调用,而且alpha测试的开销也不小,因此我们不能一直开着。
注意:通过混合操作也可以实现相同的效果,但是我们要优先使用clip函数:
接下来我们将介绍如何实现雾化效果,实现雾化效果的流程如下:
\[foggedColor = litColor + s(fogColor - litColor) = (1-s)litColor + s·fogColor
\]
参数s的范围为[0 , 1]之间,由一个函数确定(该函数的参数为摄像机位置、被雾气覆盖物体表面点)。参数s的定义如下:
\[s = saturate((dist(p,E) - fogStart)/fogRange)
\]
其中saturate会将其参数限制在范围[0, 1]之间,dist(p, E)表示表面点p到摄像机位置E之间的距离。
当dist(p, E)小于fogStart的时候,雾色将不会改变物体顶点的本色,即s = 0;
\[foggedColor = litColor;
\]
当dist(p, E)大于fogEnd(fogStart + fogRange)时,雾色将会完全遮住物体,即s = 1;
\[foggedColor = fogColor;
\]
下面的代码将展示如何实现雾化效果,我们先计算距离,然后再像素层级进行插值,最后求出光照颜色。
// Default.hlsli文件
// 光照数量的默认值
#ifndef NUM_DIR_LIGHTS
#define NUM_DIR_LIGHTS 3
#endif
#ifndef NUM_POINT_LIGHTS
#define NUM_POINT_LIGHTS 0
#endif
#ifndef NUM_SPOT_LIGHTS
#define NUM_SPOT_LIGHTS 0
#endif
// 包含光照所用的结构体和函数
#include "LightingUtil.hlsli"
Texture2D gDiffuseMap : register(t0);
SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamplerState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);
// 每一帧都在变化的常量数据
cbuffer cbPerObject : register(b0)
{
float4x4 gWorld;
float4x4 gTexTransform;
};
// 绘制过程中所使用的杂项常量数据
cbuffer cbPass : register(b1)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float3 gEyePosW;
float cbPerObjectPad1;
float2 gRenderTargetSize;
float2 gInvRenderTargetSize;
float gNearZ;
float gFarZ;
float gTotalTime;
float gDeltaTime;
float4 gAmbientLight;
// 允许应用程序在每一帧都能改变雾化效果的参数
// 比如我们可能只在一天中的特定时间才使用雾化效果
float4 gFogColor;
float gFogStart;
float gFogRange;
float2 cbPerObjectPad2;
Light gLights[MaxLights];
};
//每种材质中不同的常量数据
cbuffer cbMaterial : register(b2)
{
//漫反射反照率
float4 gDiffuseAlbedo;
//介质的一种属性
float3 gFresnelR0;
//粗造程度
float gRoughness;
//材质变换矩阵
float4x4 gMatTransform;
};
struct VertexIn
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 TexC : TEXCOORD;
};
struct VertexOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3 NormalW : NORMAL;
float2 TexC : TEXCOORD;
};
// VS.hlsl文件
#include "Default.hlsli"
VertexOut VS(VertexIn vin)
{
VertexOut vout = (VertexOut)0.0f;
//将顶点变换到世界空间
float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
vout.PosW = posW.xyz;
//假设进行的是等比变换,否则要使用世界矩阵的逆转置矩阵
vout.NormalW = mul(vin.NormalL, (float3x3) gWorld);
//将顶点变换到齐次裁剪空间
vout.PosH = mul(posW, gViewProj);
//为三角形插值输出顶点属性
float4 texC = mul(float4(vin.TexC, 1.0f, 1.0f), gTexTransform);
vout.TexC = mul(texC, gMatTransform).xy;
return vout;
}
// PS.hlsl文件
#include "Default.hlsli"
float4 PS(VertexOut pin) : SV_Target
{
float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo;
//alpha通道测试
#ifndef ALPHA_TEXT
clip(diffuseAlbedo.a - 0.1f);
#endif
//对法线插值可能导致其非规范化,所以需要重新对他进行规范化处理
pin.NormalW = normalize(pin.NormalW);
//光线经过物体表面一点反射到观察点这一方向上的向量
float3 toEyeW = gEyePosW - pin.PosW;
float distToEye = length(toEyeW);
//规范化处理
toEyeW /= distToEye;
float4 ambient = gAmbientLight * diffuseAlbedo;
const float shininess = 1.0f - gRoughness;
Material mat = { diffuseAlbedo, gFresnelR0, shininess };
float3 shadowFactor = 1.0f;
float4 directLight = ComputeLighting(gLights, mat, pin.PosW, pin.NormalW, toEyeW, shadowFactor);
float4 litColor = ambient + directLight;
#ifndef FOG
float fogAmount = saturate((distToEye - gFogStart) / gFogRange);
litColor = lerp(litColor, gFogColor, fogAmount);
#endif
//从漫反射反照率中获取alpha常用的手段
litColor.a = diffuseAlbedo.a;
return litColor;
}
在演示程序中,我们通过向CompileShader函数提供下列D3D_SHADER_MACRO结构体来开启雾化效果
const D3D_SHADER_MACRO defines[] =
{
"FOG","1",
NULL,NULL
};
mShadersp["opaquePS"] = d3dUtil::CompileShader(L"HLSL\\PS.hlsl",defines,"PS","ps_5_1");
演示程序运行效果:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章