Color Blending

Color Blending

在environment mapping和fog effects中,在生成最终的颜色时,就对颜色进行了blend(混合)操作。而且这种混合是在相同的pixel shader中执行的,因此在render target中不会出现两种独立的颜色;而一种混合色。但是color blending是一种不同的混合形式,应用于当frame buffer中某个指定pixel已经有了一个颜色值,而且为了把两种colors混合到一起,该pixel会被绘制两次。使用DirectX的blend states可以指定具体的混合方式。
在HLSL中,BlendState数据类型与前面effects中所使用的RasterizerState类型类似,只是具有不同的成员。在深入学习如何创建并使用blend state之前,需要先描述一些术语。在color blending中,将要被写入到pixel中的新的颜色称为source color,在render target中已经存在的颜色称为destination color。使用下面的公式把source和destination color混合在一起:
(source * sourceBlendFactor)blendOperation(dest * destBlendFactor)
每一个blend factor都使用表8.1中的某个值,而blend operation则使用表8.2的某个值。

表8.1 Blend Factor Options

表8.2 Blend Operation Options


注意
还有一些blend factors支持dual-source color(两个source color)混合,但是超出了本节所讨论的主题。要获取关于dual-source color混合的完整列表和描述,可以查看DirectX的文档。


关于如何使用blend factor和blend operation枚举变量,表8.3列出了一些常用的组合方法。

表8.3 Common Color Blending Settings


从表中可以看到,Additive Blending简单地把source color和destination color相加,Multiplication Blending是把他们相乘。一个2倍的Multiplication Blending使用DEST_COLOR作为source blend factor而不是把该factor置为0。
Alpha Blending使用source alpha通道(SRC_ALPHA)作为乘法因子把source color和destination color进行混合,得到一种透明的效果。与environment mapping和fog中所讨论的插值一样,也可以把source color的alpha通道值看成一个百分比滑块,通过该值确定每一种color所占的比例。比如,如果source alpha值为1.0,那么最终颜色中source color占了100%的比例,而destination color就没有影响。如果source alpha值为0.7,则最终的结果中source color占了70%,destination color占了30%。

列表8.4列出了使用alpha blending effect的代码。但是为了使effect更加有趣,该effect中针对alpha通道使用了一个独立的纹理transparency map。通过让alpha texture在运行期间动态变化,可以产生一些非常有趣的结果。这里列出了该effect的全部代码,包括ambient lighting,point light,specular highlighting,fog。
列表8.4 TransparencyMapping.fx

#include "include\\Common.fxh"

/************* Resources *************/

cbuffer CBufferPerFrame
{
	float4 AmbientColor : AMBIENT <
		string UIName =  "Ambient Light";
		string UIWidget = "Color";
	> = {1.0f, 1.0f, 1.0f, 0.0f};

	float4 LightColor : COLOR <
		string Object = "LightColor0";
		string UIName =  "Light Color";
		string UIWidget = "Color";
	> = {1.0f, 1.0f, 1.0f, 1.0f};

	float3 LightPosition : POSITION <
		string Object = "PointLight0";
		string UIName =  "Light Position";
		string Space = "World";
	> = {0.0f, 0.0f, 0.0f};

	float LightRadius <
		string UIName =  "Light Radius";
		string UIWidget = "slider";
		float UIMin = 0.0;
		float UIMax = 100.0;
		float UIStep = 1.0;
	> = {10.0f};

	float3 FogColor <
		string UIName =  "Fog Color";
		string UIWidget = "Color";
	> = {0.5f, 0.5f, 0.5f};

	float FogStart = { 20.0f };
	float FogRange = { 40.0f };
	
	float3 CameraPosition : CAMERAPOSITION < string UIWidget="None"; >;
}

cbuffer CBufferPerObject
{
	float4x4 WorldViewProjection : WORLDVIEWPROJECTION < string UIWidget="None"; >;
	float4x4 World : WORLD < string UIWidget="None"; >;
	
	float4 SpecularColor : SPECULAR <
		string UIName =  "Specular Color";
		string UIWidget = "Color";
	> = {1.0f, 1.0f, 1.0f, 1.0f};

	float SpecularPower : SPECULARPOWER <
		string UIName =  "Specular Power";
		string UIWidget = "slider";
		float UIMin = 1.0;
		float UIMax = 255.0;
		float UIStep = 1.0;
	> = {25.0f};
}

Texture2D ColorTexture <
	string ResourceName = "default_color.dds";
	string UIName =  "Color Texture";
	string ResourceType = "2D";
>;

Texture2D TransparencyMap <
	string UIName =  "Transparency Map";
	string ResourceType = "2D";
>;

SamplerState TrilinearSampler
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};

RasterizerState DisableCulling
{
	CullMode = NONE;
};

BlendState EnableAlphaBlending
{
	BlendEnable[0] = True;
	SrcBlend = SRC_ALPHA;
	DestBlend = INV_SRC_ALPHA;
};

/************* Data Structures *************/

struct VS_INPUT
{
	float4 ObjectPosition : POSITION;
	float2 TextureCoordinate : TEXCOORD;
	float3 Normal : NORMAL;
};

struct VS_OUTPUT
{
	float4 Position : SV_Position;
	float3 Normal : NORMAL;
	float2 TextureCoordinate : TEXCOORD0;	
	float4 LightDirection : TEXCOORD1;
	float3 ViewDirection : TEXCOORD2;
	float FogAmount : TEXCOORD3;
};

/************* Utility Functions *************/

float get_fog_amount(float3 viewDirection, float fogStart, float fogRange)
{
	return saturate((length(viewDirection) - fogStart) / (fogRange));
}

/************* Vertex Shader *************/

VS_OUTPUT vertex_shader(VS_INPUT IN, uniform bool fogEnabled)
{
	VS_OUTPUT OUT = (VS_OUTPUT)0;
	
	OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);
	OUT.TextureCoordinate = get_corrected_texture_coordinate(IN.TextureCoordinate);
	OUT.Normal = normalize(mul(float4(IN.Normal, 0), World).xyz);
	
	float3 worldPosition = mul(IN.ObjectPosition, World).xyz;	
	
	OUT.LightDirection = get_light_data(LightPosition, worldPosition, LightRadius);
	float3 viewDirection = CameraPosition - worldPosition;	
	
	if (fogEnabled)
	{		
		OUT.FogAmount = get_fog_amount(viewDirection, FogStart, FogRange);
	}
	
	OUT.ViewDirection = normalize(viewDirection);

	return OUT;
}

/************* Pixel Shader *************/

float4 pixel_shader(VS_OUTPUT IN, uniform bool fogEnabled) : SV_Target
{
	float4 OUT = (float4)0;
		
	float3 normal = normalize(IN.Normal);
	float3 viewDirection = normalize(IN.ViewDirection);
	float4 color = ColorTexture.Sample(TrilinearSampler, IN.TextureCoordinate);
	float3 ambient = get_vector_color_contribution(AmbientColor, color.rgb);

	LIGHT_CONTRIBUTION_DATA lightContributionData;
	lightContributionData.Color = color;
	lightContributionData.Normal = normal;
	lightContributionData.ViewDirection = viewDirection;
	lightContributionData.SpecularColor = SpecularColor;
	lightContributionData.SpecularPower = SpecularPower;	
	lightContributionData.LightDirection = IN.LightDirection;
	lightContributionData.LightColor = LightColor;
	float3 light_contribution = get_light_contribution(lightContributionData);
	
	OUT.rgb = ambient + light_contribution;
	OUT.a = TransparencyMap.Sample(TrilinearSampler, IN.TextureCoordinate).a;

	if (fogEnabled)
	{
		OUT.rgb = lerp(OUT.rgb, FogColor, IN.FogAmount);
	}
	
	return OUT;
}

technique10 alphaBlendingWithFog
{
	pass p0
	{
		SetVertexShader(CompileShader(vs_4_0, vertex_shader(true)));
		SetGeometryShader(NULL);
		SetPixelShader(CompileShader(ps_4_0, pixel_shader(true)));         
		
		SetRasterizerState(DisableCulling);
		SetBlendState(EnableAlphaBlending, (float4)0, 0xFFFFFFFF);
	}
}

technique10 alphaBlendingWithoutFog
{
	pass p0
	{
		SetVertexShader(CompileShader(vs_4_0, vertex_shader(false)));
		SetGeometryShader(NULL);
		SetPixelShader(CompileShader(ps_4_0, pixel_shader(false)));                
			
		SetRasterizerState(DisableCulling);
		SetBlendState(EnableAlphaBlending, (float4)0, 0xFFFFFFFF);
	}
}

Transparency Mapping Effect

TransparencyMapping.fx中的大部分代码你应该都见过,仅管是分布在不同的effects中。其中,新增了一个BlendState类型的对象EnableAlphaBlending。设置SrcBlend = SRC_ALPHA,DestBlend = INV_SRC_ALPHA用于alpha blending,通过设置BlendEnable[0] = True把第一个render target与output-merger阶段绑定,就启动了color blending。Output-merger阶段最多可以同时绑定8个render targets。在第四部分,“Intermediate-Level Rendering Topics”将会讨论多个render targets。
在一种technique里面调用SetBlendState()函数就可以使用BlendState对象。函数的第一个参数就是要使用的BlendState对象。第二参数是一个颜色值常量,用于当source或destination blend factor中任何一个factor被设置为BLEND_FACTOR值的情况。第三个参数是一个32位的multisample coverage mask值,该参数值用于确定有效的render targets中哪些采样会被更新。第11章,“Direct3D Initialization”将会讲解multisampling。


Transparency Mapping Output

图8.8显示了在一个plane上使用transparency mapping effect的输出结果,该plane使用了一种checkerboard color texture,并被一个skybox所包围。在每张输出结果的图片下面,是对应的用于alpha channel的texture。在左图中,alpha map由完全透明渐变到完全不透明(alpha值从0.0渐变为1.0)。在灰度图中,看起来就像是从黑色(0.0)渐变成白色(1.0)。在右图中,plane object有一个明显的single-pixel宽的边框,因为在NVIDIA FX Composer的Render panel中选中了该plane。这是为了演示透明效果,并不是渲染错误。

图8.8 TransparencyMapping.fx applied to a plane with a checkerboard color texture
using a gradient alpha map (left) and an alpha map in the shape of a maple leaf (right).
(Skybox texture by Emil Persson.)
Alpha blending依赖于所渲染objects的顺序。透明的objects应该以从后到前的顺序(从距离camera最远到距离最近的顺序)进行渲染;否则,“bleed through”(渗透出来的)背景pixels就是错误的。图8.9中显示了plane位于sphere前面的同一个场景中,使用两种绘制objects的遍历顺序得到的输出结果。在两图中,render target都是先被设置为灰色,再绘制参考表格。之后,在左图中,先绘制environment-mapped sphere再绘制alpha-blended plane。在右图中,则是先绘制plane,再绘制sphere。注意右图中,输出结果是混合了灰色背景色以及表格而不是sphere,之所以sphere没有被绘制到render target上,是因为plane是blended。

图8.9 A scene depicting the impact of draw order on alpha-blended objects. On the left,
the sphere is rendered before the plane, and vice versa for the image to the right. (Skybox
texture by Emil Persson.)

注意
在NVIDIA FX Composer中objects的绘制顺序是由场景中objects创建的顺序决定的。场景中最早创建的object会最先被绘制。

对于不透明的图片,颜色就不会受到绘制顺序的影响,但是从前往后绘制不透明的objects可以带来性能上的提升。在第四部分将会讨论这个问题。

总结

本意讲述了一些新的图形学技术。包括texture cubes以及它的应用,skybox和environment mapping;还有fog effect和color blending。最终,编写了一个transparency mapping effect,并讨论了有关绘制顺序的问题。
下一章,将讨论normal mapping和displacement mapping,这也是第二部分,“Shader Authoring with HLSL”的最后一章。


Exercises

1. Create a texture map, either by hand or using a tool such as Terragen from Planetside
Software. Use the texture map with the skybox effect, and observe the output as you
manipulate the camera.
2. Experiment with the environment mapping effect. Modify the ambient and environment light
values and the reflection amount, and observe the results.
3. Implement a complete fog effect that incorporates a single directional light.
4. Explore the transparency mapping effect with multiple objects in a scene. Vary the creation
order of objects, and observe the results.


1.手动或者使用一个工具(比如Planetside Software的Terragen)创建一个texture map。把该texture map应用到skybox effect中,调整camera并观察输出结果。
2.测试environment mapping effect。尝试修改ambient和environment light值,以及reflection amout,并观察输出结果。
3.实现一个完整的fog effect,并在场景上加入一个directional light。
4.尝试在一个包含多个objects的场景中使用tranparency mapping effect。使用多种不同的顺序创建objects,并观察输出结果。

本章配套学习代码:

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值