• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

3D API Skinned animating

Status
Not open for further replies.
Level 10
Joined
Sep 14, 2007
Messages
227
I got quite a small problem about OpenGl, Directx11 shaders. To begin with, few weeks ago I just created skinned animating. However now I got one problem, because models without skinning are scaled by 0 and that's means they are not shown in screen. In order to avoid this I need to create somekind of protection and I have two ways (atleast I think there's two ways).
////////////////////////////////////////////////////////////////////////////////
// Filename: texture.vs
////////////////////////////////////////////////////////////////////////////////


/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;

matrix boneMatrix[100];
};

cbuffer CameraBuffer
{
float3 cameraPosition;
float padding;
};

//////////////
// TYPEDEFS //
//////////////

struct VertexInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD;
float3 normal : NORMAL;
float3 tangent : TANGENT;
float3 binormal : BINORMAL;
int4 boneId : BONEID;
float4 boneWeight : BONEWEIGHT;
};

struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD;
float3 normal : NORMAL;
float3 tangent : TANGENT;
float3 binormal : BINORMAL;
int4 boneId : BONEID;
float4 boneWeight : BONEWEIGHT;
float3 viewDirection : TEXCOORD2;
};

////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType LightVertexShader(VertexInputType input)
{
PixelInputType output;
float4 worldPosition;

input.position.w = 1.0f;

//#if (ZERO == 1)
//if (input.boneWeight[0] + input.boneWeight[1] + input.boneWeight[2] + input.boneWeight[0] != 0)
//{
matrix boneTransformation = boneMatrix[input.boneId[0]] * input.boneWeight[0];
boneTransformation += boneMatrix[input.boneId[1]] * input.boneWeight[1];
boneTransformation += boneMatrix[input.boneId[2]] * input.boneWeight[2];
boneTransformation += boneMatrix[input.boneId[3]] * input.boneWeight[3];

input.position = mul(input.position, boneTransformation);
input.normal = mul(input.normal, boneTransformation);
//}
//endif
//#endif


output.position = mul(input.position, worldMatrix);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);

// Store the input color for the pixel shader to use.
output.tex = input.tex;

output.normal = mul(input.normal, (float3x3)worldMatrix);
output.normal = normalize(output.normal);

output.tangent = mul(input.tangent, (float3x3)worldMatrix);
output.tangent = normalize(output.tangent);

output.binormal = mul(input.binormal, (float3x3)worldMatrix);
output.binormal = normalize(output.binormal);

worldPosition = mul(input.position, worldMatrix);

output.viewDirection = normalize(cameraPosition.xyz - worldPosition.xyz);

return output;
}


1. Use simple if, but I think its slow, stupid and I never saw in anybodies shader if function in bones.
2. Create shader for every model with defined options #if. But I think this way is very unpractical. By the way I saw Heroes of Newerth using #if for bones, but does't means they create for every mesh in scene new shader program?
// (C)2009 S2 Games
// mesh_color.vsh
//
// Default mesh vertex shader
//=============================================================================

//=============================================================================
// Uniform variables
//=============================================================================
uniform mat4 mWorldViewProj;
uniform mat4 mWorldView;
uniform mat4 mWorldViewOffset; // World * View Offset
uniform mat3 mWorldRotate; // World rotation for normals

uniform vec4 vColor;

uniform vec3 vSunPositionWorld;

uniform vec3 vAmbient;
uniform vec3 vSunColor;

uniform float fFogStart;
uniform float fFogEnd;
uniform float fFogScale;
uniform float fFogDensity;
uniform vec3 vFog;


#if (SHADOWS == 1)
uniform mat4 mLightWorldViewProjTex; // Light's World * View * Projection * Tex
#endif

#if (NUM_BONES > 0)
#ifdef NON_SQUARE_MATRIX
uniform mat3x4 vBones[NUM_BONES];
#else
uniform vec4 vBones3[NUM_BONES * 3];
#endif
#endif

#ifdef CLOUDS
uniform mat4 mCloudProj;
#endif

#if (FOG_OF_WAR == 1)
uniform mat4 mFowProj;
#endif

//=============================================================================
// Varying variables
//=============================================================================
varying vec4 v_vColor;

#if (LIGHTING_QUALITY == 0)
varying vec3 v_vNormal;
varying vec3 v_vTangent;
varying vec3 v_vBinormal;
#elif (LIGHTING_QUALITY == 1)
varying vec3 v_vHalfAngle;
#ifdef GROUND_AMBIENT
varying vec4 v_vSunLight;
#else
varying vec3 v_vSunLight;
#endif
#elif (LIGHTING_QUALITY == 2)
#ifdef GROUND_AMBIENT
varying vec4 v_vDiffLight;
#else
varying vec3 v_vDiffLight;
#endif
#endif

#if (LIGHTING_QUALITY == 0 || FALLOFF_QUALITY == 0)
varying vec3 v_vPositionOffset;
#endif

#if (SHADOWS == 1)
varying vec4 v_vTexcoordLight;
#endif

#ifdef CLOUDS
varying vec2 v_vClouds;
#endif

#if ((FOG_QUALITY == 1 && FOG_TYPE != 0) || (FALLOFF_QUALITY == 1 && (FOG_TYPE != 0 || SHADOWS == 1)) || FOG_OF_WAR == 1)
varying vec4 v_vData0;
#endif

//=============================================================================
// Vertex attributes
//=============================================================================
attribute vec4 a_vPosition;
attribute vec3 a_vNormal;
attribute vec4 a_vTexCoord0;
attribute vec4 a_vTangent;

#if (NUM_BONES > 0)
attribute vec4 a_vBoneIndex;
attribute vec4 a_vBoneWeight;
#endif


//=============================================================================
// Vertex Shader
//=============================================================================
void main()
{
#if ((FOG_QUALITY == 1 && FOG_TYPE != 0) || (FALLOFF_QUALITY == 1 && (FOG_TYPE != 0 || SHADOWS == 1)) || FOG_OF_WAR == 1)
v_vData0 = vec4(0.0, 0.0, 0.0, 0.0);
#endif

vec3 vInNormal = (a_vNormal / 255.0) * 2.0 - 1.0;
vec4 vInTangent = (a_vTangent / 255.0) * 2.0 - 1.0;

#if (NUM_BONES > 0)
vec4 vPosition;
vec3 vNormal;
vec3 vTangent;

//
// GPU Skinning
// Blend bone matrix transforms for this bone
//

vec4 vBlendIndex = a_vBoneIndex;
vec4 vBoneWeight = a_vBoneWeight;

#ifdef NON_SQUARE_MATRIX
mat3x4 mBlend = mat3x4(0.0);
for (int i = 0; i < NUM_BONE_WEIGHTS; ++i)
mBlend += vBones[int(vBlendIndex)] * vBoneWeight;
#else
mat4 mBlend = mat4(0.0);
mBlend[3][3] = 1.0;
for (int i = 0; i < NUM_BONE_WEIGHTS; ++i)
{
mBlend[0] += vBones3[int(vBlendIndex) * 3 + 0] * vBoneWeight;
mBlend[1] += vBones3[int(vBlendIndex) * 3 + 1] * vBoneWeight;
mBlend[2] += vBones3[int(vBlendIndex) * 3 + 2] * vBoneWeight;
}
#endif

vPosition = vec4((a_vPosition * mBlend).xyz, 1.0);
vNormal = normalize((vec4(vInNormal, 0.0) * mBlend).xyz);
vTangent = normalize((vec4(vInTangent.xyz, 0.0) * mBlend).xyz);
#else
vec4 vPosition = a_vPosition;
vec3 vNormal = vInNormal;
vec3 vTangent = vInTangent.xyz;
#endif


vec3 vPositionOffset = (mWorldViewOffset * vPosition).xyz;

gl_Position = mWorldViewProj * vPosition;
#if (WATER_QUALITY == 0)
gl_ClipVertex = mWorldView * vPosition;
#else
#ifdef REFLECTIONS
gl_ClipVertex = mWorldView * vPosition;
#endif
#endif
gl_TexCoord[0] = a_vTexCoord0;
v_vColor = vColor;

#if (LIGHTING_QUALITY == 0)
v_vNormal = mWorldRotate * vNormal;
v_vTangent = mWorldRotate * vTangent;
v_vBinormal = cross(v_vTangent, v_vNormal) * vInTangent.w;
#elif (LIGHTING_QUALITY == 1)
vec3 vCamDirection = -normalize(vPositionOffset);
vec3 vLight = vSunPositionWorld;
vec3 vWVNormal = mWorldRotate * vNormal;
vec3 vWVTangent = mWorldRotate * vTangent;
vec3 vWVBinormal = cross(vWVTangent, vWVNormal) * vInTangent.w;

mat3 mRotate = mat3(vWVTangent, vWVBinormal, vWVNormal);

v_vSunLight.xyz = vLight * mRotate;

#ifdef GROUND_AMBIENT
v_vSunLight.w = dot(vWVNormal, vec3(0.0, 0.0, 1.0)) * 0.375 + 0.625;
#endif

v_vHalfAngle = normalize(vLight + vCamDirection) * mRotate;
#elif (LIGHTING_QUALITY == 2)
vec3 vLight = vSunPositionWorld;
vec3 vWVNormal = mWorldRotate * vNormal;

float fDiffuse = max(dot(vWVNormal, vLight), 0.0);

v_vDiffLight.xyz = vSunColor * fDiffuse;

#ifdef GROUND_AMBIENT
v_vDiffLight.w = dot(vWVNormal, vec3(0.0, 0.0, 1.0)) * 0.375 + 0.625;
#endif
#endif

#if (LIGHTING_QUALITY == 0 || FALLOFF_QUALITY == 0)
v_vPositionOffset = vPositionOffset;
#endif

#if (SHADOWS == 1)
v_vTexcoordLight = mLightWorldViewProjTex * vPosition;
#endif

#ifdef CLOUDS
v_vClouds = (mCloudProj * vPosition).xy;
#endif

#if (FALLOFF_QUALITY == 1 && (FOG_TYPE != 0 || SHADOWS == 1))
v_vData0.z = length(vPositionOffset);
#endif

#if (FOG_QUALITY == 1 && FOG_TYPE != 0)
#if (FOG_TYPE == 0)
// FOG_NONE
v_vData0.w = 0.0;
#elif (FOG_TYPE == 1)
// FOG_LINEAR
v_vData0.w = clamp(length(vPositionOffset) * vFog.x + vFog.y, 0.0, 1.0) * vFog.z;
#elif (FOG_TYPE == 2)
// FOG_EXP2
v_vData0.w = 1.0 - exp2(-length(vPositionOffset) * fFogDensity);
#elif (FOG_TYPE == 3)
// FOG_EXP
v_vData0.w = 1.0 - exp(-length(vPositionOffset) * fFogDensity);
#elif (FOG_TYPE == 4)
// FOG_HERMITE
v_vData0.w = smoothstep(fFogStart, fFogEnd, length(vPositionOffset)) * fFogScale;
#endif
#endif

#if (FOG_OF_WAR == 1)
v_vData0.xy = (mFowProj * vPosition).xy;
#endif
}
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
1. Use simple if, but I think its slow, stupid and I never saw in anybodies shader if function in bones.
It should not be that slow due to pipelining as long as the condition does not occur very often. However if it does, then it will likely be quite slow since pipelining will break down.

2. Create shader for every model with defined options #if. But I think this way is very unpractical. By the way I saw Heroes of Newerth using #if for bones, but does't means they create for every mesh in scene new shader program?
Shaders are notorious for taking ages to compile. I am pretty sure most games now use shader caches to try and mitigate this problem. I do not think it is viable to use a different shader for each model. Let us not forget that having many shaders loaded at once might lower efficiency.

You seem to be missing the obvious CPU based solutions to this I believe. 2 shaders could be defined (one for no skinned and one for skinned models) and you separate the rendering of such into parallel and merge the results.

You could also define all models with no skin to have dummy bone data (on the CPU, before being sent to the GPU) which is nothing more than an identity matrix so that multiplication by 0 does not occur. Sure this may not be optimum for such models but it will allow you to use the same shader for all models and also keep the render optimized for models with skins.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
From a short read on msdn it seems like a way to batch state changes (and this batch is probably optimized by the driver).
What exactly is your question?

You will need two shaders either way, one for static models, and one for animated ones.

For the viewer I went with the path of having one shader and making static models only use the identity matrix, but for an application where you have many models, this is not very ideal.
 
Level 10
Joined
Sep 14, 2007
Messages
227
What exactly is your question?
I'm going to create a 3d egine on Directx 11 and my question would be, what is the best method to create shader and for static and bone animated?
1. Using techniques?
2. Macros(defines) ( but this one is better for game options i suppose)?
3. Or just two shaders?

You will need two shaders either way, one for static models, and one for animated ones.

For the viewer I went with the path of having one shader and making static models only use the identity matrix, but for an application where you have many models, this is not very ideal.
I don't get this part... So u saying that I should use two shaders, but then it's not efficient for big engine? Or i'm wrong?
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
You are mixing things up.

Techniques (from the little I read) are merely a way to have pre-defined state changes in a batch.
It's the same as you making Direct3D calls, but I am assuming the driver can optimize it better, since it knows before-hand what you want (plus it allows for artists to define settings with no need for a programmer, and no need to recompile the project).
By state changes I mean setting the shader, blend modes, alpha tests, and so on.

Macros are pre-processor statements.
If you have a #ifdef, you don't magically have a shader that branches by itself, you need to #define it = you need to have two shaders, one with the #define, one without.
If you do want branching, you want a normal condition, if, however this should be avoided as much as you can (having extra if's will probably be slower than changing shaders).

It's important to note that in this context a shader is an actual shader unit running on your graphics card. I don't mean the source. If you have #ifdefs in your source, you can create multiple shader units out of that source, depending on what you #define at run-time.

Using one shader, where it always does skinning, is not ideal when you are going to have many static models, since you are wasting a lot of processing on them.
I meant that for the 3D viewer, I did do this, but the viewer only renders one model (= a couple of meshes, not a big deal).
If you have many models, a lot of which are probably going to be static, it is preferred to have two shaders, sort all your models by their type, and render them in order (e.g. render all the static models with the simple static shader, then all the skinned models with the more complicated skinning shader).
 
Level 10
Joined
Sep 14, 2007
Messages
227
K ty :). Too bad I can't give u rep (I need to spread some before giving you...).

By the way I think i'm going to create two shaders(static and skinned) with defines in this way I will avoid creating two shader files.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Following on the previous post, look at the GLSL shader you posted.
All those #ifdefs are mapped to in-game settings and map settings, and so on.

For example, "LIGHTING_QUALITY" most likely refers to the graphics settings. When the game creates the shader, it plugs the appropriate #define according to your settings.

"NUM_BONES" is probably used to both control static/skinned shader, plus it gives the maximum amount of bones your specific computer can have (= maximum uniform vector slots your GPU supports minus the slots the shader uses).

And so on.
All of these parameters are defined at run-time, so that the source can be easily kept in one place, and yet support everything they want.
 
Status
Not open for further replies.
Top