Code Samples
Curved Motion Streak
The following function is a vertex shader in hlsl for motion blur. In addition to manipulating vertices to create a motion streak, I was challenged to create a streak that curved according to to object's motion. This function uses the mathematics of circles to create a curved motion streak that would in post-processing be blurred on top of the scene.
// HLSL vertex shader to create a curved motion streak using
// the previous transformation and previous previous
// transformation
Vs_Output VS_MotionBlur(VS_Input IN)
{
Vs_Output vs_out;
float eps = 0.0001; //A really small value
float3 changepos = float3(0,0,0); // represents the vertex's change in position
//Put normal in view space
vs_out.normal = normalize(mul(worldView, IN.normal));
float4 currPos = mul(world, float4(IN.vertexPos, 1)); // Current transformation
float4 prevPos = mul(prevWorld, float4(IN.vertexPos, 1)); //Previous transformation
float3 motion1 = currPos.xyz - prevPos.xyz; // Difference vector, representing the motion
float len1 = length(motion1); // Length of motion
if (abs(len1) > eps){
float cDot = dot(motion1, vs_out.normal) / len1 ;
// Only manipulate vertices that are "in the back" of the object's motion. You only want
// a streak behind the object
if ( cDot < 0){
cDot = abs(cDot);
float lenarc;
//Previous previous transformation
float4 prevprevPos = mul(prevprevWorld, float4(IN.vertexPos, 1));
float3 motion2 = prevPos.xyz - prevprevPos.xyz; //Previous motion vector
float len2 = length(motion2);
float3 v = cross(motion2, motion1); // vector to represent the circle's normal
if (length(v) > eps){
v = normalize(v);
// A midpoint of the motion, used like a chord of the circle
float3 p1 = currPos - (motion1 / 2);
// Vector that intersects circle's center
float3 n1 = cross(motion1, v);
n1 = normalize(n1);
// A midpoint of the motion, used like a chord of the circle
float3 p2 = prevPos - (motion2 / 2);
// Vector that intersects circle's center
float3 n2 = cross(motion2, v);
n2 = normalize(n2);
float n1n2len = max(length(cross(n1, n2)), eps);
float t1 = length(cross((p2 - p1), n2)) / n1n2len; // Intersection of two rays
float3 center = p1 - t1 * n1; // Center point for the circle
float3 radius = currPos - center; // Calculate circle's radius
float r = t1;
changepos = cross(v, radius); // represents the vertex's change in position
len1 = (len2 > eps) ? len1 * len2 : len1; // Only use len1 if len2 is too small
// Calculate angle to rotate along the circle (determines length of streak)
float angle = k * len1 * cDot * radians(180) * perDeltaT;
lenarc = r * 2 * sin(angle); // length of the arc, determined by angle
//axis angle rotation
changepos = changepos * cos(angle) + v * dot(v, changepos) * (1 - cos(angle))
+ cross(changepos, v) * sin(angle);
changepos = normalize(changepos);
}
else{
lenarc = cDot * 2 * k * perDeltaT; // Just use straight line as backup
}
// change the vertice's position based on values calculated
currPos = currPos - (float4(changepos, 0) * lenarc);
}
}
vs_out.vertexPos = mul(viewProj, currPos);
vs_out.color = float4( 0.8f, 0.9f, 1.0f, 1);
vs_out.tex = IN.tex;
return vs_out;
}
Post-process Draw
The following function is the Draw function used for a post-process technique class. It was designed to be generic for many kinds of post-processes, so there are variables that describe which render targets to use as input, which textures to output to (including back buffer), and which technique to use. Types of post-processes include things like "Gaussian blur", "Depth of Field", and "Motion Blur".
// Draw function for a post-process technique
//
// input: data stores the render targets and other information
// pScreenVB is the vertex buffer of the screen quad
// ClearColor is the clean color of the surface buffer
//
// output: output textures are changed by shader technique
void PostProcess::Draw(ShaderData* data, IDirect3DVertexBuffer9 *pScreenVB, DWORD ClearColor){
// If outputting to back buffer, set render target to the back buffer.
// Otherwise, set to the post-process surface
if (output_method_ & TOBACKBUFFER)
(*d3ddev_)->SetRenderTarget(0,*(data->b_buffer_));
else
(*d3ddev_)->SetRenderTarget(0, surface_);
// Initiate directX device, sprite device, FVF, vertex buffer of screen,
// and the technique of the post-process
(*d3ddev_)->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, ClearColor, 1.0f, 0);
(*d3ddev_)->BeginScene();
(*d3dspt_)->Begin(D3DXSPRITE_ALPHABLEND);
(*d3ddev_)->SetFVF(CUSTOMFVF);
(*d3ddev_)->SetStreamSource(0,pScreenVB,0,sizeof(SVertex));
(*d3deffect_)->SetTechnique(technique_.c_str());
// Set all the input textures
// strShaderTex is an array of strings storing the name of texures in the shader
for (int sTex = 0; sTex < NUM_SHADERTEX; ++sTex){
// Use main rendering texture (Usually updated at the end of groups of post-processes
if (input_method_[sTex] & WITHTEXMAIN)
(*d3deffect_)->SetTexture(strShaderTex[sTex].c_str(), *(data->texMain_));
//Use secondary rendering texture
else if (input_method_[sTex] & WITHTEXRENDER)
(*d3deffect_)->SetTexture(strShaderTex[sTex].c_str(), *(data->texRender_));
//Use one of the other rendering targets
else{
bool isApplied = false;
for (int i = 0; i < data->numtexs_ && !isApplied; ++i){
int select = 1 << (NUMWITHTEXS + i);
if (input_method_[sTex] & select){ // Use appropriate texture
(*d3deffect_)->SetTexture(strShaderTex[sTex].c_str(), *(data->tex_[i]));
isApplied = true;
}
}
}
}
// Draw onto a textured quad representing the screen
unsigned int numPasses;
(*d3deffect_)->Begin(&numPasses, NULL); // begin using the effect
for (unsigned int uPass = 0; uPass < numPasses; ++uPass){
(*d3deffect_)->BeginPass(uPass); // begin the pass
(*d3ddev_)->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2);
(*d3deffect_)->EndPass(); // end the pass
}
(*d3deffect_)->End();
// Output to back buffer
if (!(output_method_ & TOBACKBUFFER)){
(*d3ddev_)->EndScene();
(*d3dspt_)->End();
}
//Output to another specified texture
if (output_method_ & TOTEXMAIN)
data->texMain_ = &texture_;
if (output_method_ & TOTEXRENDER)
data->texRender_ = &texture_;
for (int i = 0; i < data->numtexs_; ++i){
int select = 1 << (i + NUMTOTEXS);
if (output_method_ & select)
data->tex_[i] = &texture_;
}
}