← 返回项目列表
DX11 PBR 渲染器
对于基于图像的照明(IBL),我使用了split sum approximation,第一部分是预过滤的环境图,其中我根据不同的粗糙度值预先计算了 HDR 图像的卷积。对于第二部分,即镜面积分的 BRDF 部分,我使用了预先计算的LUT。水平纹理坐标为 n⋅ωi,垂直纹理坐标为粗糙度值。
DX11HLSLPBRIBLCell Shading
DX11 PBR渲染器
基于图像的照明

具有不同Texure的 IBL
float4 CalcIBLPBRPointLight( float3 texNormal, float3 fragPos, float3 viewDir, v2p_t input )
{
float3 albedo = albedoTexture.Sample(diffuseSampler, input.uv).rgb;
float metallic = metallicTexture.Sample(diffuseSampler, input.uv).r;
float roughness = roughnessTexture.Sample(diffuseSampler, input.uv).r;
float ao = aoTexture.Sample(diffuseSampler, input.uv).r;
float3 N = texNormal;
float3 V = viewDir;
float3 R = reflect(-V, N);
float3 F0 = float3(0.04, 0.04, 0.04);
F0 = lerp(F0, albedo, metallic);
float3 Lo = float3(0.f, 0.f, 0.f);
for(int i = 0; i < NUM_LIGHTS; i++)
{
float3 L = normalize(PointLights[i].LightPosition - fragPos);
float3 H = normalize(L + V);
float distanceToPointLight = distance(PointLights[i].LightPosition, fragPos);
float attenuation = 1.f / ( distanceToPointLight * distanceToPointLight );
float3 radiance = PointLights[i].PointLightColor * attenuation;
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
float3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
float3 kS = F;
float3 kD = float3(1.f, 1.f, 1.f) - kS;
kD *= 1.0 - metallic;
float3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.f) * max(dot(N, L), 0.f) + 0.0001f;
float3 specular = numerator / denominator;
float NdotL = max(dot(N, L), 0.f);
Lo += (kD * albedo / PI + specular) * radiance * NdotL;
}
float3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);
float3 kS = F;
float3 kD = 1.0 - kS;
kD *= 1.0 - metallic;
float3 irradiance = irradianceMap.Sample(diffuseSampler, N).rgb;
float3 diffuse = irradiance * albedo;
const float MAX_REFLECTION_LOD = 4.0;
float3 prefilteredColor = prefilteredMap.SampleLevel(diffuseSampler, R, roughness * MAX_REFLECTION_LOD).rgb;
float2 envBRDF = BRDF.Sample(diffuseSampler, float2(max(dot(N, V), 0.0), roughness)).rg;
// Environment Bidirectional reflectance distribution function
float3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y);
float3 ambient = (kD * diffuse + specular) * ao;
float3 color = ambient +Lo;
return float4(color, 1.0f);
}
IBL 的 HLSL 代码
Physically-Based Rendering
每个 PBR 对象都需要 5 个Texture:Albedo, Normal, Metallic, Roughness, Ambient Occlusion。对于每个Texture,我使用不同的Slot来存储所有纹理。如果某些对象缺少某些Texture,我会将纯白色纹理 RGBA(255、255、255、255)传递给着色器,以防最终结果太暗

PBR 管线中的Texture List
void Prop::Render() const
{
if (g_theRenderer != nullptr)
{
g_theRenderer->BindShader( g_theApp->m_diffuseShader );
g_theRenderer->SetRasterizerMode( RasterizerMode::SOLID_CULL_BACK );
if (m_material != nullptr)
{
g_theRenderer->SetMaterialConstants( m_material->m_materialType, m_material->m_materialAmbient, m_material->m_materialDiffuse, m_material->m_materialSpecular, m_material->m_materialShininess, m_material->m_materialRoughness, m_material->m_materialMetallic );
}
g_theRenderer->SetModelConstants( GetModelMatrix( m_position, m_orientation, m_scale ), m_color );
g_theRenderer->BindTexture( m_texture );
g_theRenderer->BindTexture( m_normalMap, 1 );
g_theRenderer->BindTexture( m_albedoMap, 2 );
g_theRenderer->BindTexture( m_aoMap, 3 );
g_theRenderer->BindTexture( m_metallicMap, 4 );
g_theRenderer->BindTexture( m_roughnessMap, 5 );
g_theRenderer->BindTexture( g_BRDFTexture, 6 );
m_game->m_hdrCubemap->BindHDRCubeMap();
g_theRenderer->DrawVertexBuffer( m_vertexBuffer, (int)m_vertexes.size(), 0 , VertexType::PCUTBN );
//Draw outline
if (m_isOutline)
{
g_theRenderer->SetRasterizerMode( RasterizerMode::SOLID_CULL_FRONT );
g_theRenderer->BindShader( m_game->m_outlineShader );
g_theRenderer->BindTexture( nullptr );
g_theRenderer->SetModelConstants( GetModelMatrix( m_position, m_orientation, m_scale * 1.02f ), Rgba8( 255, 138, 48, 255 ) );
g_theRenderer->DrawVertexBuffer( m_vertexBuffer, (int)m_vertexes.size(), 0, VertexType::PCUTBN );
}
}
}
Slots 中绑定不同Texure的代码
在 BRDF 的镜面部分,D 使用Trowbridge-Reitz GGX,F 使用 Fresnel-Schlick 近似,G 使用 Smith’s Schlick-GGX。

不同粗糙度和金属度的 PBR
float4 CalcPBRPointLight( float3 normal, float3 fragPos, float3 viewDir, v2p_t input )
{
float4 pointLightColor = float4(0.f, 0.f, 0.f, 0.f);
float3 albedo = float3(0.5f, 0.0f, 0.0f);
float ao = 1.f;
float metallic = MatMetallic;
float roughness = MatRoughness;
float3 N = normal;
float3 V = viewDir;
float3 F0 = float3(0.04f, 0.04f, 0.04f );
F0 = lerp(F0, albedo, metallic);
float3 Lo = float3(0.f, 0.f, 0.f);
for(int i = 0; i < NUM_LIGHTS; i++)
{
float3 L = normalize(PointLights[i].LightPosition - fragPos);
float3 H = normalize(L + V);
float distanceToPointLight = distance(PointLights[i].LightPosition, fragPos);
float attenuation = 1.f / ( distanceToPointLight * distanceToPointLight );
float3 radiance = PointLights[i].PointLightColor * attenuation;
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
float3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0);
float3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.f) * max(dot(N, L), 0.f) + 0.0001f;
float3 specular = numerator / denominator;
float3 kS = F;
float3 kD = float3(1.f, 1.f, 1.f) - kS;
kD *= 1.0 - metallic;
float NdotL = max(dot(N, L), 0.f);
Lo += (kD * albedo / 3.14159265f + specular) * radiance * NdotL;
}
float3 ambient = float3(0.03f, 0.03f, 0.03f) * albedo * ao;
float3 color = ambient + Lo;
color = color / (color + float3(1.f, 1.f, 1.f));
color = pow(color, float3(1.f/2.2f, 1.f/2.2f, 1.f/2.2f));
pointLightColor = float4(color.xyz, 1);
return pointLightColor;
}
PBR 的 HLSL 代码
int rowNum = 7;
int columnNum = 7;
float cuurentMetallic = 0.f;
float currentRoughness = 0.f;
for (int i = 0; i < rowNum; i++)
{
cuurentMetallic = GetClamped((float)i / (float)rowNum, 0.05f, 1.f );
for (int j = 0; j < columnNum; j++)
{
currentRoughness = (float)j / (float)columnNum;
SphereProp* spherePropPBR = new SphereProp( this );
spherePropPBR->m_isMaterialAdjustable = true;
spherePropPBR->m_radius = 0.5f;
spherePropPBR->m_material = new Material( MaterialType::PBR, 1.f, 1.f, 1.f, 64.f, cuurentMetallic, currentRoughness );
spherePropPBR->m_position = Vec3( -15.f, -3.f, 2.f ) + Vec3( 0.f, (float)i * 1.2f, (float)j * 1.2f );
spherePropPBR->m_vertexes.reserve( 2000 );
AddVertsForSphere3D( spherePropPBR->m_vertexes, Vec3( 0.f, 0.f, 0.f ), spherePropPBR->m_radius, Rgba8::RED, AABB2::ZERO_TO_ONE, sphereSlices );
spherePropPBR->StartUp();
m_sphereProps.push_back( spherePropPBR );
m_allProps.push_back( spherePropPBR );
}
}
创建具有不同粗糙度和金属度的物体代码
着色模型
一些着色模型(Cell Shading、BackLight、OrenNayar 等)
float4 CalcFresnelBandedDirLight( float3 normal, float3 viewDir )
{
//Directional Light-------------------------------------------------------------------
// specular shading
float3 directionLightReflectDir = reflect(SunDirection, normal);
float directionLightSpec = pow(max(dot(viewDir, directionLightReflectDir), 0.0), MatShininess)/0.032f;
//Directional Light
float ambient = AmbientIntensity * MatAmbient;
float DiffuseReflection = (dot(normal, -SunDirection) + 1.f) * 0.5f;
float Layered = 4.f;
float dotValue = floor(DiffuseReflection * Layered) / Layered;
float diffuse = SunIntensity * dotValue * MatDiffuse;
//Fresnel
float F0 = 0.05f;
float specular = 0.f;
specular += FresnelSchlickMethod(F0, normal, viewDir, 3);
specular += SpecularIntensity * directionLightSpec * MatSpecular;
//Direction result
float4 lightColor = float4((ambient + diffuse + specular).xxx, 1);
return lightColor;
}
float4 CalcBackLightDirLight( float3 normal, float3 viewDir )
{
//Directional Light-------------------------------------------------------------------
// specular shading
float3 directionLightReflectDir = reflect(SunDirection, normal);
float directionLightSpec = saturate(pow(max(dot(viewDir, directionLightReflectDir), 0.f), MatShininess));
float ambient = AmbientIntensity * MatAmbient;
//wrap
float WrapValue = 1.2f;
float DiffuseReflection = dot(normal, -SunDirection);
float dotValue = max((DiffuseReflection + WrapValue) / (1.f + WrapValue), 0.0);//[-1,1] => [0,1]
//SSS simulation
float SSSValue = 1.3f;
float TransmissionIntensity = 2.f;
float TransmissionScope = 3.f;
float3 LightNormalizeValue = -normalize(normal * SSSValue + SunDirection);
dotValue = dotValue + pow(saturate(dot(LightNormalizeValue, viewDir)), TransmissionScope) * TransmissionIntensity;
float diffuse = SunIntensity * dotValue * MatDiffuse;
float specular = 0.f;
float F0 = 0.05f;
specular += FresnelSchlickMethod(F0, normal, viewDir, 3);
specular += SpecularIntensity * directionLightSpec * MatSpecular;
//Direction result
float4 lightColor = float4((ambient + diffuse + specular).xxx, 1);
return lightColor;
}
float4 CalcOrenNayarDirLight( float3 normal, float3 viewDir )
{
//Directional Light
float ambient = AmbientIntensity * MatAmbient;
float NormalLight = saturate(pow(max(dot(normal, -SunDirection), 0.0), 2.f));
float NormalView = saturate(dot(normal, viewDir));
float Phiri = length(viewDir - normal * NormalView) + length(-SunDirection - normal * NormalLight);
float ACosNormalView = acos(NormalView);//[0,1]
float ACosNormalLight = acos(NormalLight);
float Alpha = max(ACosNormalView, ACosNormalLight);
float Beta = min(ACosNormalView, ACosNormalLight);
float MatRoughness = 1.f - saturate(MatShininess/100.f);
float MyRoughness = pow(MatRoughness, 2);
float A = 1 - 0.5f * (MyRoughness / (MyRoughness + 0.33f));
float B = 0.45f * (MyRoughness / (MyRoughness + 0.09f));
float dotValue = NormalLight * (A + B * max(0, Phiri) * sin(Alpha) * tan(Beta));
float diffuse = SunIntensity * dotValue * MatDiffuse;
//Direction result
float4 lightColor = float4((ambient + diffuse).xxx, 1);
return lightColor;
}
其他着色模型的 HLSL 代码
截图


