Kỹ thuật ánh xạ bóng phụ thuộc vào thực tế là bóng đổ của mỗi pixel chỉ thể hiện các đỉnh gần máy ảnh nhất. Do đó, chỉ những phần vật thể gần nhất với "nguồn sáng" mới được vẽ vào vùng bóng và lưu vào bộ đệm khi thể hiện bóng đổ của ảnh. Khi chúng ta đang vẽ các đối tượng của mình từ điểm quan sát, chúng ta có thể xác định khoảng cách thực tế của mỗi đỉnh từ nguồn sáng, sau đó chúng ta có thể so sánh với giá trị được lưu trữ trong bộ đệm sâu. Nếu giá trị được lưu trong bộ đệm độ sâu nhỏ hơn khoảng cách thực tế của đỉnh từ nguồn sáng, thì nó phải có một vật thể nằm giữa nó và máy ảnh và điểm đó cần thể hiện bóng đổ Khoảng cách từ nguồn sáng đến mặt phẳng Khoảng cách từ nguồn sáng đến vật
Để nhận được giá trị lưu trữ trong bộ nhớ cho các điểm đã vẽ, chúng ta phải ánh xạ hình ảnh chính xác cho từng điểm, sau đó thêm vào bóng của điểm bằng cách gọi thủ tục PPModel.fx effect nghĩa là copy hiệu ứng bóng thêm vào các tham số tọa độ và độ dài của bóng
bool DoShadowMapping = true; float4x4 ShadowView; float4x4 ShadowProjection; texture2D ShadowMap;
sampler2D shadowSampler = sampler_state { texture = <ShadowMap>; minfilter = point; magfilter = point; mipfilter = point; }
Thủ tục VertexShaderOutput struct sẽ thêm giá trị vị trí không gian của đỉnh, của nguồn sáng
float4 ShadowScreenPosition : TEXCOORD2; Bóng đổ của đỉnh cần tính toán các giá trị sau:
output.ShadowScreenPosition = mul(mul(input.Position, World), mul(ShadowView, ShadowProjection));
Sau đó chúng ta cần lấy mẫu của bề mặt vật, để tạo biên của bóng cho phù hợp với hình ảnh thực tế chúng ta quan sát bằng mắt thường. Vấn đề là lấy đủ số mẫu cần thiết để bóng được trơn nhưng cũng không quá nhiều mẫu làm giảm tốc độ xử lý
float sampleShadowMap(float2 UV) {
if (UV.x < 0 || UV.x > 1 || UV.y < 0 || UV.y > 1) return 1;
return tex2D(shadowSampler, UV).r; }
Cuối cùng chúng ta dùng hàm để xác định kích thước từ độ sâu bề mặt vật ứng với bóng đổ của điểm
float2 shadowTexCoord = postProjToScreen(input.ShadowScreenPosition) + halfPixel();
float mapDepth = sampleShadowMap(shadowTexCoord);
Nếu chúng ta nhận giá trị trả về giá trị 1 nghiã là sẽ tạo bóng đổ cho điểm xét trên bề mặt vật thể
return float4(mapDepth, mapDepth, mapDepth, 1);
Trước khi sử dụng hàm này để lấy giá trị trả về để sinh ra bóng đổ, ta cần chắc chắn rằng ta đã thiết lập các tham số ngưỡng để xác định độ sâu bề mặt vật
prepareMainPass() function of PrelightingRenderer: if (part.Effect.Parameters["DoShadowMapping"] != null) part.Effect.Parameters["DoShadowMapping"].SetValue(DoShadowMappi ng); if (!DoShadowMapping) continue; if (part.Effect.Parameters["ShadowMap"] != null) part.Effect.Parameters["ShadowMap"].SetValue(shadowDepthTarg); if (part.Effect.Parameters["ShadowView"] != null) part.Effect.Parameters["ShadowView"].SetValue(shadowView); if (part.Effect.Parameters["ShadowProjection"] != null) part.Effect.Parameters["ShadowProjection"]. SetValue(shadowProjection);
Bóng đổ mờ
Khi tạo bóng như thuật toán trên, bóng tạo ra rất chuẩn nhưng đôi khi chưa phản ánh đúng thực tế của thể giới thực mà thực tế có sự giao thoa ánh sáng, nhiễu nên bóng thường có độ nhòe thậm chí hai bóng riêng biệt khi có hai nguồn sáng chói phân biệt. Tác dụng của xử lý mờ bóng (soft shadow), khi biểu diễn các vật thể trên bề mặt có nền phức tạp đảm bảo biểu diễn chính xác
Hình 2.15 Bóng đổ mờ
Như vậy, bề mặt vật có hai giá trị, giá trị bao phủ bề mặt bằng một vật liệu nào đó, và giá trị bóng. Quá trình làm mờ là tính toán giá trị trung bình các pixel và các điểm láng giếng. Hàm Gausian được sử dụng để tính toán như sau:
// The texture to blur texture ScreenTexture;
sampler2D tex = sampler_state {
texture = ; minfilter = point; magfilter = point; mipfilter = point; };
// Precalculated weights and offsets
float weights[15] = { 0.1061154, 0.1028506, 0.1028506, 0.09364651, 0.09364651, 0.0801001, 0.0801001, 0.06436224, 0.06436224,
0.04858317, 0.04858317, 0.03445063, 0.03445063, 0.02294906, 0.02294906 };
float offsets[15] = { 0, 0.00125, -0.00125, 0.002916667, -0.002916667, 0.004583334, -0.004583334, 0.00625, -0.00625, 0.007916667, - 0.007916667, 0.009583334, -0.009583334, 0.01125, -0.01125 }; // Blurs the input image horizontally float4 BlurHorizontal(float4 Position :
POSITION0, float2 UV : TEXCOORD0) : COLOR0 { float4 output = float4(0, 0, 0, 1);
// Sample from the surrounding pixels using the precalculated
// pixel offsets and color weights for (int i = 0; i < 15; i++) output +=
tex2D(tex, UV + float2(offsets[i], 0)) * weights[i]; return output; } // Blurs the input image vertically float4 BlurVertical(float4 Position : POSITION0, float2 UV : TEXCOORD0) : COLOR0 { float4 output = float4(0, 0, 0, 1); for (int i = 0; i < 15; i++) output += tex2D(tex, UV + float2(0, offsets[i])) * weights[i]; return output; }
technique Technique1 {
pass Horizontal {
PixelShader = compile ps_2_0 BlurHorizontal(); }
pass Vertical {
PixelShader = compile ps_2_0 BlurVertical(); }
Ở đây, có hai kỹ thuật, làm mờ theo chiểu ngang và làm mờ theo chiều dọc. Chúng ta làm mờ theo mỗi chiều độc lập với nhau (mỗi bóng đổ của pixel thêm vào giá trị điều chỉnh căn cứ vào độ lệch của giá trị trung bình. Ví dụ làm mờ theo chiều dọc thêm vào giá trị tính được từ 15 điểm láng giềng: SpriteBatch spriteBatch;
RenderTarget2D shadowBlurTarg; Effect shadowBlurEffect;
Những giá trị này có thể được tạo ra từ thủ tục:
spriteBatch = new SpriteBatch(GraphicsDevice); shadowBlurEffect = Content.Load("GaussianBlur"); shadowBlurTarg = new
RenderTarget2D(GraphicsDevice, shadowMapSize, shadowMapSize, false, SurfaceFormat.Color, DepthFormat.Depth24);
Từ đó thủ tụclàm mờ:
void blurShadow(RenderTarget2D to, RenderTarget2D from, int dir) {
// Set the target render target graphicsDevice.SetRenderTarget(to); graphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque); // Start the Gaussian blur effect
shadowBlurEffect.CurrentTechnique.Passes[dir].Apply(); // Draw the contents of the source render target so they can
// be blurred by the gaussian blur pixel shader spriteBatch.Draw(from, Vector2.Zero, Color.White); spriteBatch.End();
// Clean up after the sprite batch graphicsDevice.BlendState = BlendState.Opaque; graphicsDevice.DepthStencilState = DepthStencilState.Default;
Việc điều chỉnh cuối cùng đối với lớp Prelightingrenderer, ta cần đảm bảo làm mờ được hàm Draw(). Trước hết copy từ ảnh nền đến ảnh bóng mờ theo chiều ngang sau đó theo chiều dọc:
public void Draw() { drawDepthNormalMap(); drawLightMap(); if (DoShadowMapping) { drawShadowDepthMap(); blurShadow(shadowBlurTarg, shadowDepthTarg, 0); blurShadow(shadowDepthTarg, shadowBlurTarg, 1); } prepareMainPass(); }