This project is read-only.

Coloring the spheres in the partitioning tutorial with different colors

Jul 17, 2009 at 6:01 AM
Edited Jul 22, 2009 at 2:59 PM

Hello,

I was working on the partitioning tutorial that came with xen. I have created a similar project with about 1900 spheres. My requirement is that each of that sphere has to be colored with a different shade of color. 

For the same I removed the attached .fx file and created a MaterialShader array and assigned it to this.shader. The material shader array contains a list of material shaders with differnt colored lights. In the draw call, I bind the appropriate materialshader to draw a sphere of a specific color, but I cannot achieve the desired effect.

Can I use white light and create a sphere with different color or is there another way I can achieve my goal?

Jul 17, 2009 at 4:19 PM
Edited Jul 17, 2009 at 4:20 PM

Here is ColorSphere Class. (From Tutorials 02, and I edited it.)

---------------------------------------------------------------------------------

public class ColorSphere : IDraw
    {
        Vector3 position;
        Vector3 size;
        Color color;
        Sphere sphere;
        IShader shader;
        Matrix worldMatrix;

        public ColorSphere(Color color, Vector3 position, Vector3 size)
        {
            this.position = position;
            this.color = color;
            this.size = size;
            this.worldMatrix = Matrix.CreateTranslation(position);

            sphere = new Sphere(size, 32);

            MaterialShader material = new MaterialShader();

            Vector3 lightDirection = new Vector3(0.5f, 1, -0.5f);

            material.Lights = new MaterialLightCollection();
            material.UsePerPixelSpecular = true;
            material.Lights.AmbientLightColour = color.ToVector3() * 0.5f;
            material.Lights.AddDirectionalLight(true, lightDirection, Color.White);
            material.Lights.AddDirectionalLight(true, -lightDirection, Color.DarkSlateBlue);
            material.SpecularColour = Color.White.ToVector3(); 

            this.shader = material;

        }

        public void Draw(DrawState state)
        {
            state.PushWorldMatrixMultiply(ref worldMatrix);
            shader.Bind(state);
            sphere.Draw(state);
            state.PopWorldMatrix();
        }

        public bool CullTest(ICuller culler)
        {
            return culler.TestSphere(size.Length(), position);
        }
    }

-------------------------------------------------------------------------------

 

You can use this class --- for example,

sphere1 = new ColorSphere(Color.Black, new Vector3(0, 0, 0), new Vector3(1,1,1));

sphere2 = new ColorSphere(Color.Green, new Vector3(5, 3, 1), new Vector3(1,1,1));

sphere3 = new ColorSphere(Color.Red, new Vector3(-1, 5, 4), new Vector3(1,1,1));

...

 

 

Jul 21, 2009 at 2:24 AM

Hi Aasim, if you are feeling brave, there is another possibility. It'll be *fast*, but very limited and tricky.

The shader that was included with the partitioning and instancing example was setup to draw the spheres as instances or individually, for both the xbox and PC's.
To do this as instanced, the sphere geometry was drawn as a batch, passing in an array of world matrices for the spheres.

The thing is, this is the format of a world matrix is something like this:

X.x X.y X.z 0
Y.x Y.y Y.z 0
Z.x Z.y Z.z 0
P.x P.y P.z 1

where P is the position, X/Y/Z are the rotation axis. You notice that there are 4 constant values there. Because they are constant, you could use these to pass in the colour of your sphere.
So:

X.x X.y X.z R
Y.x Y.y Y.z G
Z.x Z.y Z.z B
P.x P.y P.z 1

So in the 'AddInstance(DrawState)' method, you could add:

instanceMatrices[instanceCount].M14 = colour.R;
instanceMatrices[instanceCount].M24 = colour.G;
instanceMatrices[instanceCount].M34 = colour.B;

After the call to state.GetWorldMatrix(out instanceMatrices[instanceCount]);. 
By doing that, you'd be storing the colour in the instance data.

It would then be a matter of modifying the shader, so the code that extracts the world matrix in the shader, would change from:

float4x4 instanceWorld = float4x4(worldX,worldY,worldZ,worldW);

to something like:

float4x4 instanceWorld = float4x4(worldX.xyz, 0,worldY.xyz, 0,worldZ.xyz, 0, worldW);

You could then extract the colour:

float3 colour = float3(worldX.w,worldY.w,worldZ.w);

The trickier bit would be the non-instancing shader, which already provides the world matrix. In hlsl, accessing a matrix element is by using '_m00' notation. So the red value would be worldMatrix._m03.
Anyway. If you feel up to it, it might be a bit of fun. :-)

Jul 21, 2009 at 9:08 AM

@Status Unknown

Your method works perfectly well for me, but I am getting problems implementing it.

I made all the changes exactly as you suggested, but the colour of the spheres didnt change as desired. When I used to extract the colors from the matrix and set it to the colour variable nothing happened, and the colour stayed red. Then, I removed the GLOBAL semantic from the colour variable, but still I couldnt reassign it.

I am assigning the colour variable (float3 colour = float3(worldX.w,worldY.w,worldZ.w)) in the Tutorial16_VS_Instance and Tutorial16_PS_Instance, am I supposed to do that?

Please suggest me a solution for that. 

Jul 21, 2009 at 8:36 PM
Edited Jul 22, 2009 at 3:00 PM

@StatusUnknown

Thank you, I finally got it... you see I am a bit new to XEN so had a little problem implementing your solution :)

neways, the way I got it is that I had the vertex shader return the colour, and the pixel shader take it as a parameter.

Jul 22, 2009 at 6:23 AM
Edited Jul 22, 2009 at 3:01 PM

Another problem :)

I now have a working solution, but there is still one tiny problem. I have created color 10 shades, ranging from vector4(1,0,0,1) to vector4(1,0.9,0.9,1). But when this renders on the screen, I can only see 3 shades, one dark red, one white and one pink. BTW I have 1 directional white light. 

Am I missing something?? how should I get all the 10 shades ( and probably more in the future)??

Jul 24, 2009 at 2:07 PM
Edited Jul 24, 2009 at 2:09 PM

If you are using the shader method I mentioned, then that is entirely independent of any lighting shaders (MaterialShader). In which case it would be a problem with how the algorithm is being implemented.
Sorry there isn't much I can help you with. But it sounds like only the red value is getting values in the 0-1 range, and the G/B values are either 1 or far larger.

I did warn it wasn't an easy technique :-) It's something that would require quite a bit of understanding of 'behind the scenes' graphics pipeline activity. Which your earlier comment suggests to me you don't have just yet. So I'll be honest and suggest you should ignore the method I suggested and go with something simpler and easier to manage such as what kkc0923 suggested, even though it'll potentially be less efficient.