Thứ Năm, 30 tháng 5, 2013

Thursday Rotate, Hide & Delete Em

Almost Deferred

I was all set to start some deferred rendering code when I realized that my editor still had a few missing ingredients. It could not control the limb visibility of the new instance stamp objects, it did not respect the rotation of the object either and you could not delete the object from the edited scene. Essential features you will agree.

Hide, Rotate and Delete

It took some time to ensure the base functionality was in place, and then it was a quick step to re-compile the editor, first making sure the limb visibility was set on the instance object before making it into an instance stamp, and then sitting back and watching it happen.

It was quite a buzz seeing the logic that once served a different object handling system work nicely with the new one, as internally it could not be more different to what we had before.

Hiding limbs and rotating objects, a key need within the editor, where to some degree anticipated and they presented no problems. Delete however was a minefield..

So much of a minefield that it's not entirely perfect (or finished) yet, as this process involves the pain of locating the mesh data within the buffer, deleting it and then shuffling the remaining valid mesh data (vertex and index data). There are also multiple instances in the editor that add and remove the objects so I need to catch each type and ensure the right data is produced.

Further, as I expand the capabilities of the instance stamp system further, my ancient reliance on the MAP() array inside the current FPSC code is also starting to appear quite redundant. No sense storing this reference data twice, once in DBP arrays and once in the DBP object engine. My thinking is to detect and remove all reliance on the DBP array side, effectively deleting all references to this map array so that when I choose to increase the level size, whatever that becomes, I don't need to worry about fixed size arrays in the actual DBP engine code. It's a big job though so going to tackle that when other more identifiable tasks have been put to bed.

Deferred Sample Selected

Many coders learn more from a simple cut and paste example than any amount of books and documentation on the subject, and I am one of them. I have now selected my uber simple demo and it looks something like this:


That's right, the old teapot makes a comeback once again.  This little fellow taught me DirectX in the early years :)  What this new demo does is produce a very simple four render target example of deferred rendering, and does so almost entirely within a single shader. I have copied the code below, so I hope the author does not mind (credited below), so you can see how simple it really is:

//-------------------------------------------------------------------------------
//           Name: deferred.fx
//         Author: Nikita Kindt (nk47@bk.ru)
//  Last Modified: 17/06/06
//-------------------------------------------------------------------------------

float4x3 c_mWorld;
float4x3 c_mView;
float4x3 c_mViewInverse;
float4x4 c_mProjection;
float4x4 c_mViewProjection;

// light parameters
float3 c_vLightDir;
float4 c_vLightPos;
float4 c_vLightDiffuse;
float4 c_vLightSpecular;

// material parameters
float4 c_vMaterialDiffuse;
float4 c_vMaterialSpecular;
float c_fSpecularPower;

// textures
texture2D c_tDiffuseMap;
texture2D c_tSceneMaterialMap;
texture2D c_tSceneNormalMap;
texture2D c_tScenePositionXYMap;
texture2D c_tScenePositionZMap;


sampler DiffuseSampler = sampler_state
{
Texture = <c_tDiffuseMap>;

AddressU = Wrap;
AddressV = Wrap;

MagFilter = Linear;
MinFilter = Linear;
MipFilter = Linear;
};

// rendertargets
sampler SceneMaterialSampler = sampler_state
{
Texture = <c_tSceneMaterialMap>;
MagFilter = Point;
MinFilter = Point;
};

sampler SceneNormalSampler = sampler_state
{
Texture = <c_tSceneNormalMap>;
MagFilter = Point;
MinFilter = Point;
};

sampler ScenePositionXYSampler = sampler_state
{
Texture = <c_tScenePositionXYMap>;
MagFilter = Point;
MinFilter = Point;
};

sampler ScenePositionZSampler = sampler_state
{
Texture = <c_tScenePositionZMap>;
MagFilter = Point;
MinFilter = Point;
};



struct VS_INPUT_BUILD
{
    float3  vPos            : POSITION0;
    float2  vTex0           : TEXCOORD0;
    float3  vNrm            : NORMAL0;
    float3  vTan            : TANGENT0;
    float3  vBin            : BINORMAL0;
};

struct VS_OUTPUT_BUILD
{
float4 vPos : POSITION0;
    float2  vTex0           : TEXCOORD0;
float3 vWorldPos : TEXCOORD1;
float3 vWorldNrm : TEXCOORD2;
};

struct PS_OUTPUT_BUILD
{
float4 vMaterial : COLOR0;
float4 vWorldNrm : COLOR1;
float4 vWorldPosXY : COLOR2;
float4 vWorldPosZ : COLOR3;
};

VS_OUTPUT_BUILD vsBuild(VS_INPUT_BUILD i)
{
VS_OUTPUT_BUILD o;

o.vWorldPos = mul(float4(i.vPos, 1), c_mWorld);
o.vPos = mul(float4(o.vWorldPos, 1), c_mViewProjection);
o.vTex0 = i.vTex0;
o.vWorldNrm = normalize(mul(float4(i.vNrm, 0), c_mWorld));

return o;
};

// psBuild()
// put geometry data into render targets
PS_OUTPUT_BUILD psBuild(VS_OUTPUT_BUILD i) : COLOR0
{
PS_OUTPUT_BUILD o;

// material
float4 vDiffuseMaterial = tex2D(DiffuseSampler, i.vTex0);

o.vMaterial.rgb = vDiffuseMaterial;
o.vMaterial.a = 1.0;

// convert normal to texture space [-1;+1] -> [0;1]
o.vWorldNrm.xyz = i.vWorldNrm * 0.5 + 0.5;
o.vWorldNrm.w = 0.0;

// position
o.vWorldPosXY = float4(i.vWorldPos.xy, 0, 0);
o.vWorldPosZ = float4(i.vWorldPos.z, 0, 0, 0);

return o;
};



struct PS_INPUT_LIGHT
{
float2 vTex0 : TEXCOORD0;
};

// psLighting()
// uses data from textures (previous render targets)
float4 psLighting(PS_INPUT_LIGHT i) : COLOR0
{
float3 vDiffuseMaterial = tex2D(SceneMaterialSampler, i.vTex0).rgb;
float3 vSpecularMaterial = tex2D(SceneMaterialSampler, i.vTex0).a;

// normals are stored in texture space [0,1] -> convert them back to [-1,+1] range
float3 vWorldNrm = (tex2D(SceneNormalSampler, i.vTex0) - 0.5) * 2;

float3 vWorldPos;
vWorldPos.xy = tex2D(ScenePositionXYSampler, i.vTex0).xy;
vWorldPos.z = tex2D(ScenePositionZSampler, i.vTex0).x;
float3 vLightDir = normalize(c_vLightPos - vWorldPos);
float3 vEyeVec = normalize(c_mViewInverse[3].xyz - vWorldPos);
float3 vDiffuseIntensity = dot(vLightDir, vWorldNrm);
float3 vSpecularIntensity = pow(max(0, dot(vEyeVec, reflect(-vLightDir, vWorldNrm))), c_fSpecularPower);

float4 color;
color.rgb = vDiffuseIntensity * c_vLightDiffuse.xyz * vDiffuseMaterial + 
vSpecularIntensity * c_vLightSpecular.xyz * vSpecularMaterial;
color.a = 1.0;

// here we add color to show how lighting pass affects the scene
color.rgb += i.vTex0.rgr * 0.5;

return color;
};


technique buildPass
{
pass p0
{
VertexShader = compile vs_1_1 vsBuild();
PixelShader = compile ps_2_0 psBuild();

CullMode = ccw;
FillMode = solid;
Zenable = true;
ZWriteEnable = true;
ZFunc = less;
StencilEnable = false;
AlphaBlendEnable = false;
AlphaTestEnable = false;
ColorWriteEnable = red | green | blue;
}
}

technique lightPass
{
    pass p0
{
VertexShader = NULL;
PixelShader = compile ps_2_0 psLighting();

CullMode = none;
FillMode = solid;
Zenable = false;
StencilEnable = false;
AlphaBlendEnable = true;
Srcblend = One;
Destblend = One;
AlphaTestEnable = false;
ColorWriteEnable = red | green | blue;
}

};

Well, maybe if you are not 100% schooled on reading shader code, it might seem a little daunting, but for anyone who has written a shader, you will start to ask what all the fuss was about.

I am not keen on using FOUR render targets, and the above example is really just a skeleton for what the final shader will end up looking like, but I really like the fact we have a VERY simple template to start from. It is much better to start from a simple shader that works to a complex shader that does not work, or worse an empty shader you have to write from scratch. If anyone knows of a good shader authoring IDE that detects compile errors in real-time and works with DirectX 9.0c shader code (i.e. VS1/PS1/PS2/PS3), send me a few links!

Signing Off

Hopefully going to get an early night today so I can be up bright and early for my drive to Crewe and train down to London. Friday I meet the secret FPSC investor and the guy you will all owe a beer to when the Reloaded project is complete. When the Kickstarter campaign failed, this amazing fellow swooped in and backed the whole project personally. The least I can do is buy the fellow a meal in our countries capital (if he lets me pay). I will be staying in London until Saturday so there will be no blog tomorrow, but I will provide a special report on my secret meeting over the weekend (or Monday), but given it's secret there will be precious little facts divulged. I will see if I can take some photos to lighten the blog and provide some relief from the sea of code I am drowning you in.

Không có nhận xét nào:

Đăng nhận xét