I wrote a real-time directx geometry shader that converts mesh objects into voxel-like cubes:
In action:
I also wrote a python version for Blender:
Code for the real-time shader… no comments, no explanation… sorry.
float Script : STANDARDSGLOBAL < string UIWidget = "none"; string ScriptClass = "object"; string ScriptOrder = "standard"; string ScriptOutput = "color"; string Script = "Technique=Lambert?Main:Main10;"; > = 0.8; //// UN-TWEAKABLES - AUTOMATICALLY-TRACKED TRANSFORMS //////////////// float4x4 WorldITXf : WorldInverseTranspose < string UIWidget="None"; >; float4x4 WvpXf : WorldViewProjection < string UIWidget="None"; >; float4x4 WorldXf : World < string UIWidget="None"; >; float4x4 ViewIXf : ViewProjection < string UIWidget="None"; >; //// TWEAKABLE PARAMETERS //////////////////// /// Point Lamp 0 //////////// float3 Lamp0Pos : Position < string Object = "PointLight0"; string UIName = "Lamp 0 Position"; string Space = "World"; > = {-0.5f,2.0f,1.25f}; float3 Lamp0Color : Specular < string UIName = "Lamp 0"; string Object = "Pointlight0"; string UIWidget = "Color"; > = {2.0f,2.0f,2.0f}; //////// COLOR & TEXTURE ///////////////////// texture ColorTexture : DIFFUSE < string ResourceName = "default_color.dds"; string UIName = "Diffuse Texture"; string ResourceType = "2D"; >; sampler2D ColorSampler = sampler_state { Texture = ; FILTER = MIN_MAG_MIP_LINEAR; AddressU = Wrap; AddressV = Wrap; }; // shared shadow mapping supported in Cg version //////// CONNECTOR DATA STRUCTURES /////////// /* data from application vertex buffer */ struct appdata { float3 Position : POSITION; float4 UV : TEXCOORD0; float4 Normal : NORMAL; float4 Tangent : TANGENT0; float4 Binormal : BINORMAL0; }; /* data passed from vertex shader to pixel shader */ struct vertexOutput { float4 HPosition : POSITION; float2 UV : TEXCOORD0; // The following values are passed in "World" coordinates since // it tends to be the most flexible and easy for handling // reflections, sky lighting, and other "global" effects. // float3 LightVec : TEXCOORD1; // float3 WorldNormal : TEXCOORD2; float3 WorldTangent : TEXCOORD3; float3 WorldBinormal : TEXCOORD4; float3 WorldView : TEXCOORD5; float3 Norm : NORMAL; //float3 Color : COLOR1; }; struct geoOutput { float4 HPosition : POSITION; float2 UV : TEXCOORD0; float4 Color : COLOR1 ; //float4 Cubes[100]; //float3 LightVec : TEXCOORD1; //float3 WorldNormal : TEXCOORD2; //float3 Norm : NORMAL; }; ///////// VERTEX SHADING ///////////////////// /*********** Generic Vertex Shader ******/ vertexOutput std_VS(appdata IN) { vertexOutput OUT = (vertexOutput)0; float4 Po = float4(IN.Position.xyz,1); OUT.HPosition = Po; OUT.UV = IN.UV; OUT.Norm = IN.Normal; //OUT.Color = tex2D(ColorSampler,IN.UV); //OUT.WorldNormal = mul(IN.Normal,WorldITXf).xyz; //OUT.Norm = IN.Normal; //float3 Pw = mul(Po,WorldXf).xyz; // OUT.LightVec = (Lamp0Pos - Pw); return OUT; } bool SameSide(float4 p1,float4 p2,float4 a,float4 b){ float3 cp1 = cross(b-a, p1-a); float3 cp2 = cross(b-a, p2-a); if(dot(cp1, cp2) >= 0){return true;} else return false; } bool PointInTriangle(float4 p, float4 a,float4 b,float4 c){ if(SameSide(p,a, b,c) && SameSide(p,b, a,c) && SameSide(p,c, a,b)) { return true; } else {return false;} } [maxvertexcount(102)] void GS( triangle vertexOutput In[3], inout TriangleStream TriStream ) { geoOutput Out; float size = .5; float UV = In[0].UV; float3 normal;// = mul((In[0].HPosition+In[1].HPosition+In[2].HPosition),0.33); float4 position;// = mul((In[0].HPosition+In[1].HPosition+In[2].HPosition),0.33); float4 wpos1 = mul(In[0].HPosition, WorldXf); float4 wpos2 = mul(In[1].HPosition, WorldXf); float4 wpos3 = mul(In[2].HPosition, WorldXf); //Trouver Ax et Bx int Ax = wpos1.x; int Bx; if(Ax>wpos2.x){ Bx = Ax; Ax = wpos2.x; } else{ Bx=wpos2.x; } if(Ax>wpos3.x){ Ax = wpos3.x; } if(Bx<wpos3.x){ Bx=wpos3.x; } //Trouver Ay et By int Ay = wpos1.y; int By; if(Ay>wpos2.y){ By = Ay; Ay = wpos2.y; } else{ By=wpos2.y; } if(Ay>wpos3.y){ Ay = wpos3.y; } if(By<wpos3.y){ By=wpos3.y; } //Trouver Az et Bz int Az = wpos1.z; int Bz; if(Az>wpos2.z){ Bz = Az; Az = wpos2.z; } else{ Bz=wpos2.z; } if(Az>wpos3.z){ Az = wpos3.z; } if(Bz<wpos3.z){ Bz=wpos3.z; } float4 cubes[100]; int numCubes = 0; /*cubes[0] = float4(Ax, Ay, Az, 1); cubes[1] = float4(Bx, By, Bz, 1); cubes[2] = wpos1; cubes[3] = wpos2; cubes[4] = wpos3;*/ //Calculer delta du plan formé par le triangle //D'abord, trouver la normale du triangle float4 edgeA = wpos2-wpos1; float4 edgeB = wpos3-wpos1; float3 wNormal = normalize(cross(edgeA,edgeB)); float delta = dot(wpos1,wNormal); //Trouver les boxels dans le triangle float3 curPos; float3 tempPos; for(int x=Ax;x<Bx+1;x++){ for(int y=Ay;y<By+1;y++){ for(int z=Az;z<Bz+1;z++){ //Trouver le point le plus proche sur le plan du triangle curPos = float3(x,y,z); tempPos = curPos+(delta-dot(curPos,wNormal))*wNormal; //garder si distance entre original et nouveau < size float tempDist = distance(curPos,tempPos); if(tempDist<(1)){ // Est-ce que le point est à l'intérieur du triangle? // (faire) if(PointInTriangle(float4(curPos,0), wpos1,wpos2,wpos3)){ cubes[numCubes]=float4(curPos,1-tempDist); numCubes++; } } } } } /*if(numCubes==0){ numCubes++; cubes[0] = float4(Ax,Ay,Az,1); }*/ //numCubes=2; for(int i=0;i<numCubes;i++){ position = float4(cubes[i].rgb,1); //Crée un cube, nécessite "position", "normal", "size". float4 pos1 = position + float4(-size, size, -size,0); float4 pos2 = position + float4(-size, -size, -size,0); float4 pos3 = position + float4(size, size, -size,0); float4 pos4 = position + float4(size, -size, -size,0); float4 pos5 = position + float4(-size, size, size,0); float4 pos6 = position + float4(-size, -size, size,0); float4 pos7 = position + float4(size, size, size,0); float4 pos8 = position + float4(size, -size, size,0); //précalculer normales et éclairage (à faire) float alpha = cubes[0].a; alpha = 1; //alpha = 0.5f; float4 couleur = float4(1,1,1,1); //face 1 edgeA = pos2-pos1; edgeB = pos3-pos1; float3 wNormal = normalize(cross(edgeA,edgeB)); float3 lightVector = normalize(Lamp0Pos - position); float diffuseValue = dot(lightVector, wNormal); diffuseValue = max(diffuseValue, 0.0); float3 couleur1 = Lamp0Color*couleur*diffuseValue; //face 2 edgeA = pos7-pos3; edgeB = pos4-pos3; wNormal = normalize(cross(edgeA,edgeB)); lightVector = normalize(Lamp0Pos - position); diffuseValue = dot(lightVector, wNormal); diffuseValue = max(diffuseValue, 0.0); float3 couleur2 = Lamp0Color*couleur*diffuseValue; //face 3 edgeA = pos8-pos4; edgeB = pos2-pos4; wNormal = normalize(cross(edgeA,edgeB)); lightVector = normalize(Lamp0Pos - position); diffuseValue = dot(lightVector, wNormal); diffuseValue = max(diffuseValue, 0.0); float3 couleur3 = Lamp0Color*couleur*diffuseValue; //face 4 edgeA = pos7-pos3; edgeB = pos1-pos3; wNormal = normalize(cross(edgeA,edgeB)); lightVector = normalize(Lamp0Pos - position); diffuseValue = dot(lightVector, wNormal); diffuseValue = max(diffuseValue, 0.0); float3 couleur4 = Lamp0Color*couleur*diffuseValue; //face 5 edgeA = pos5-pos1; edgeB = pos2-pos1; wNormal = normalize(cross(edgeA,edgeB)); lightVector = normalize(Lamp0Pos - position); diffuseValue = dot(lightVector, wNormal); diffuseValue = max(diffuseValue, 0.0); float3 couleur5 = Lamp0Color*couleur*diffuseValue; //face 6 edgeA = pos7-pos5; edgeB = pos6-pos5; wNormal = normalize(cross(edgeA,edgeB)); lightVector = normalize(Lamp0Pos - position); diffuseValue = dot(lightVector, wNormal); diffuseValue = max(diffuseValue, 0.0); float3 couleur6 = Lamp0Color*couleur*diffuseValue; //1 Out.HPosition = mul(pos4,ViewIXf); Out.Color = float4((couleur1+couleur2+couleur3)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //2 Out.HPosition = mul(pos2,ViewIXf); Out.Color = float4((couleur1+couleur3+couleur5)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //3 Out.HPosition = mul(pos3,ViewIXf); Out.Color = float4((couleur1+couleur2+couleur4)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //4 Out.HPosition = mul(pos1,ViewIXf); Out.Color = float4((couleur1+couleur4+couleur5)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //5 Out.HPosition = mul(pos5,ViewIXf); Out.Color = float4((couleur4+couleur5+couleur6)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //6 Out.HPosition = mul(pos2,ViewIXf); Out.Color = float4((couleur1+couleur3+couleur5)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //7 Out.HPosition = mul(pos6,ViewIXf); Out.Color = float4((couleur3+couleur5+couleur6)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //8 Out.HPosition = mul(pos4,ViewIXf); Out.Color = float4((couleur1+couleur2+couleur3)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //9 Out.HPosition = mul(pos8,ViewIXf); Out.Color = float4((couleur2+couleur3+couleur6)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //10 Out.HPosition = mul(pos3,ViewIXf); Out.Color = float4((couleur1+couleur2+couleur4)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //11 Out.HPosition = mul(pos7,ViewIXf); Out.Color = float4((couleur1+couleur4+couleur5)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //12 Out.HPosition = mul(pos5,ViewIXf); Out.Color = float4((couleur4+couleur5+couleur6)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //13 Out.HPosition = mul(pos8,ViewIXf); Out.Color = float4((couleur2+couleur3+couleur6)/3,alpha); Out.UV = UV; TriStream.Append( Out ); //14 Out.HPosition = mul(pos6,ViewIXf); Out.Color = float4((couleur3+couleur5+couleur6)/3,alpha); Out.UV = UV; TriStream.Append( Out ); TriStream.RestartStrip();//to end the 2nd triangle*/ } } ///////// PIXEL SHADING ////////////////////// float4 std_PS(geoOutput IN) : COLOR { /* //float3 Ln = normalize(IN.LightVec); // float3 Nn = normalize(IN.WorldNormal); float ldn = dot(Ln,Nn); ldn = max(ldn,0.0); float3 diffuseColor = (0.0f,1.0f,1.0f); // float3 diffuseColor = tex2D(ColorSampler,IN.UV).rgb; float3 result = diffuseColor * (ldn * Lamp0Color + AmbiColor); // return as float4 return float4(result,1); return float4(tex2D(ColorSampler,IN.UV).rgb,1);*/ return IN.Color*float4(tex2D(ColorSampler,IN.UV).rgb,1); //float4 bob = (.1,.1,.11); //return float4(0,1,0.5,1); } ///// TECHNIQUES ///////////////////////////// RasterizerState DisableCulling { CullMode = 1; }; DepthStencilState DepthEnabling { DepthEnable = TRUE; }; BlendState DisableBlend { BlendEnable[0] = false; }; BlendState SrcAlphaBlendingAdd { BlendEnable[0] = TRUE; SrcBlend = SRC_ALPHA; DestBlend = INV_SRC_ALPHA; BlendOp = ADD; SrcBlendAlpha = ONE; DestBlendAlpha = ONE; BlendOpAlpha = ADD; RenderTargetWriteMask[0] = 0x0F; }; technique10 Main10 < string Script = "Pass=p0;"; > { pass p0 < string Script = "Draw=geometry;"; > { SetVertexShader( CompileShader( vs_4_0, std_VS() ) ); /* ZEnable = true; ZWriteEnable = true; AlphaBlendEnable = true; SrcBlend = One; DestBlend = InvSrcColor; CullMode = CW; */ SetGeometryShader( CompileShader( gs_4_0, GS() ) ); SetPixelShader( CompileShader( ps_4_0, std_PS() ) ); SetRasterizerState(DisableCulling); SetDepthStencilState(DepthEnabling, 1); SetBlendState( SrcAlphaBlendingAdd, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); } } /////////////////////////////////////// eof //