← 返回项目列表

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 代码

截图