Jump to content
  • Advertisement
Sign in to follow this  
Scylexan

[SOLVED] [HLSL] Passing Scene Data

This topic is 4276 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

First off I'll admit that I'm new to the world of shaders and I probably picked a heafty project to learn it with; however, this project is teaching me a lot. I'm currently in the process of building a real-time raytracer fully on the GPU. With predefined scenes from arrays I have this working perfectly fine, I can even "move around" in the environment. The problem I'm having is the communication between my scene editor and passing in the scene data to the GPU. I've learned quickly that large arrays tend to not do so well on the GPU and I've read that using textures to hold scene data is a good way of overcoming this. First off, does this sound accurate and a good approach? Second, how would I approach this properly? This is what I've done so far and thought of. Within my scene editor I created a texture with the R32F format. Lock, fill data, Unlock then pass the texture to the GPU using SetValue(). The texture will only be passed once a new object is added to the scene, so I assume that I won't be hurting my performance too much with passing the texture. Also, since my scene data texture is size of my window's width I will pass to the GPU, whatever (1/width) will be. Now, this is the part that I'm not too sure on...I'll show it in code and maybe it'll explain better than I could. float normalizedTextureIndex; // (1 / width) from editor at beginning texture sceneTexture; // from editor sampler sceneSampler = sampler_state { texture = sceneTexture; // do I need anything else here? }; ...later on when needed // since my texture is R32F format, I really only need to read from the "r" float4 unitOfData = tex2D(sceneSampler, (normalizedTextureIndex * prim * N)); **NOTE: prim will be the value used to increment the texture index to the current primitive being used. **NOTE: N = [1,11] I have the scene in the texture setup as floats being: type // help determine if primitive is sphere, light, plane, etc x, y, z // for position r, g, b // for color (a is always 1.0) diffuse, specular, reflection, radiusSquared // for spheres Also, I'm using tex2D above but it was recently suggested to me to use tex2Dlod() does this sound better for my partiular situation? I realize all of this may not make much sense so if you are confused I'll try and explain areas a bit clearer. EDIT: changed "[0,10]" to "[1,11]" [Edited by - Scylexan on March 31, 2007 4:18:19 PM]

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Scylexan
sampler sceneSampler = sampler_state
{
texture = sceneTexture;
// do I need anything else here?
};


Since this is a "data texture", you don't want the GPU to perform any averaging/ect on the pixels when you sample it. So set all of the filters to point. You probably want to also clamp it.


sampler sceneSampler = sampler_state
{
texture = sceneTexture;
AddressU = CLAMP;
AddressV = CLAMP;
AddressW = CLAMP;
MIPFILTER = point;
MINFILTER = point;
MAGFILTER = point;
};


Quote:
// since my texture is R32F format, I really only need to read from the "r"
float4 unitOfData = tex2D(sceneSampler, (normalizedTextureIndex * prim * N));

In that case, you can just use float unit = tex2D(...).r;. The compiler should already optimize this out for you, but it also makes sense to do it yourself.

Quote:
Also, I'm using tex2D above but it was recently suggested to me to use tex2Dlod() does this sound better for my partiular situation? I realize all of this may not make much sense so if you are confused I'll try and explain areas a bit clearer.

It shouldn't matter, since this data texture shouldn't use mipmapping (ie only one surface in the texture).

Share this post


Link to post
Share on other sites
Hmmm, well a few days later and I'm still messing around with this. I'm still not sure if I'm writing the texture in memory correctly or sampling the texture in the shader correctly.

I switch the way I sampled the texture from:

float4 unitOfData = tex2D(sceneSampler, (normalizedTextureIndex * prim * N));

..to..

float unitOfData = (tex2Dlod(sceneSampler, (normalizedTextureIndex * prim + (N * normalizedTextureIndex))).r;


I had to switch it to tex2Dlod() cause of it being within dyanmic branches, wouldn't compile otherwise. As for adding the ".r" I thank circlesoft for that suggestion. I changed the way I index the texture cause this way seems to make more sense.

For example, let's say I have a 640x640 texture in memory with format of R32F and if I remember correctly the Pool is Managed. As mentioned in my first post this is a data texture containing my scene data so let's say we have 2 spheres in the scene:

{ 3.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 2.0, 3.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 2.0 }

Following the data I use from my first post, the above set of numbers would represent 2 spheres, first sphere at position 0,0,0 with red color having diffuse and specular highlighting, the second sphere will be at position 2,0,0 with blue color also having diffuse and specular highlighting. Both are with a radius of 2. This data starts at 0,0 position of the texture and should just fill up the texture for as many objects in the scene. Obviously there is a limit here but I don't plan to have that many objects in a scene. So, I want to be able to grab certain parts of each object when I need it. So, let's say I want the Z position of the 2nd sphere. There are 11 pieces of data for each object (0->10). Since the texture is 0 to 1 we have to normalize the size:

(1/640) => normalIndex

we'll need to know which object we're dealing with at the moment (currentObject) and which data piece we need also (dataOffset). Now, currentObject would be in increments of 11 (0, 11, 22, etc) and dataOffset would be from 0 to 10 for each piece of data. Using the dataOffset like that would throw off our sample so it *should* be multiplied by the normalIndex. So, in order to grab what we wanted before (Z position of the 2nd sphere):

((1/640) * 11) + (1 * (1/640))

more generic form:

(normalIndex * currentObject) + (dataOffset * normalIndex)

Does this make any sense or am I just completely off here? I ask cause when I load up the shader code through FX Composer and load a sample texture written to file from my editor. Using (1/640) results in nothing; however, if I use 0 for normalIndex I get a sphere but it's always in the same position and the color is always white no matter what I do to the data of the texture. If I hardcode values for 1 sphere and let it run the ray tracer works and I can even set up a small array with 2 or 3 spheres and the ray tracer works, so I know it's not that. It has to be either the way I'm sampling the data or how I'm writing the data to the texture. I'm currently using C# and when I write the data the to the texture I use unsafe code:


unsafe
{
float* pData = (float*)sceneTexture.LockRectangle(0, LockFlags.None).InternalData.ToPointer();

foreach(CustomMesh mesh in myMeshes) // Queue of CustomMeshes
{
*pData = (float)mesh.Type; pData++;
*pData = mesh.X; pData++;
*pData = mesh.Y; pData++;
*pData = mesh.Z; pData++;
*pData = (float)(mesh.Color.R / 255.0f); pData++;
*pData = (float)(mesh.Color.G / 255.0f); pData++;
*pData = (float)(mesh.Color.B / 255.0f); pData++;
*pData = mesh.Diffuse; pData++;
*pData = mesh.Specular; pData++;
*pData = mesh.Reflection; pData++;
*pData = mesh.RadiusSquared; pData++;
}

sceneTexture.UnlockRectangle(0);
myEffect.SetValue("sceneData", sceneTexture);
}

Could any of this be wrong? Is there a better way to go about any of this? Any help will be much appricated.

Share this post


Link to post
Share on other sites
...so no one really knows? Well, let me at least ask this question. What would be the best way to create a "data texture", write to it, and read from it in HLSL?

Share this post


Link to post
Share on other sites
Alright, so I had some advancement within this issue here. First, I completely forgot that the tex2Dlod() takes in a float4 not a float. So, once I changed that and placed the value I get from the above equation in the U part of the float4 I get some results.

However, I did run into a few minor issues that I think are kind of odd. First off, when I added checking of the object type (plane, sphere, light, etc) which should be the first value (0.0, only dealing with 1 object here) I don't get a 3 returned like I should. If I check against a 0 I get the sphere drawn. So the sampled data in the first value is off. Also, my color values appear to be off a bit as well, they seem to be somewhat alright for mixed up color values but solid colors like red, green, blue. I get a completely black sphere with the specular highlights.

While trying to figure out why this is happening, I decided to write my texture in memory to a file and open it up in FX Composer. I choose to save the file as a *.dds (is there a better format to test this out with?). When the texture is opened and I remove all channel views but red (texture is R32F format), I can see the written pixels. If I zoom in to get a closer look, it appears that the pixel color has bled slightly into it's neighbors. Is it possible with this "bleeding", that my values are getting slightly thrown off? Or is this just because it was written to file and put into a particular file format?

Share this post


Link to post
Share on other sites
Well, still working on this issue but I figured I'd post my current progress and hope that this all will help someone, someday.

I did have some success recently. As I mentioned, I changed the sample coordinate from a float to a float4. As I am only dealing with 1 or 2 objects at the moment I don't really alter V and only alter U of the coordinate. I found recently that it *appears* that the data is shifted a bit. I had setup before these following constants:


const static float size = 640.0;
const static float ObjType = 0.0 / size; // always 0
const static float PositionX = 1.0 / size;
const static float PositionY = 2.0 / size;
const static float PositionZ = 3.0 / size;
const static float ColorR = 4.0 / size;
const static float ColorG = 5.0 / size;
const static float ColorB = 6.0 / size;
const static float ObjDiffuse = 7.0 / size;
const static float ObjSpecular = 8.0 / size;
const static float Reflection = 9.0 / size;
const static float RadiusSquared = 10.0 / size;


I was getting the weird results with this. Like if I altered the X value the object was moving in the Y direction. Colors appeared shifted a bit and my specular highlighting appeared to not work anymore. Not to mention the type was off, I was able to narrow down that when it was a 3 it was really somewhere between 1.8 and 1.9. This didn't make any sense. When I shifted the values over to:


const static float size = 640.0;
const static float ObjType = 1.0 / size;
const static float PositionX = 2.0 / size;
const static float PositionY = 3.0 / size;
const static float PositionZ = 4.0 / size;
const static float ColorR = 5.0 / size;
const static float ColorG = 6.0 / size;
const static float ColorB = 7.0 / size;
const static float ObjDiffuse = 8.0 / size;
const static float ObjSpecular = 9.0 / size;
const static float Reflection = 10.0 / size;
const static float RadiusSquared = 11.0 / size;


I started seing better results. The type was recongized, the position movement was all correct. Diffuse and specular worked again, radius size worked. I didn't try the reflection yet. However, my colors were off a bit. After altering the color values I found that the green and blue were perfectly fine. It was my red that was missing. It wasn't off any, it just flat out would not show up. This makes even less sense. Why would everything else work but this one sampled value in the middle of my data? I know it wasn't working cause when I changed it to Red object, I was getting a black object. Any other color that doess not involve red came out fine.

This is where I'm currently stuck. I have no clue why my red is not showing up. To test it a bit more, I commented out the area where I sample for the red color value of the object and put a 1.0 there. The red showed up, with the mix of what ever the blue and green were. So, my code for painting the object is fine, it has something to do with the value the sampler is grabbing from the texture in memory.

To add a bit more confusion to the issue, I get odd results when I start to deal with more than 1 object in the scene. If it's just 1 object, I can alter all the data and it works fine (except for the red color of course); however, if I add another object to the scene from the Scene Editor I can't alter any of the data for that object. I mean, I can but it doesn't actually update the object on the screen. If I highlight my first object with 2 objects in the screen, and alter the data it alters the data for both objects. Although, the data altered for the second object is not exactly the type of data I altered from the first. Like if I alter the z position of the first object, the position and color of the second object changes. Just to make sure my code for writing the texture from C# was alright, I quickly check the values I'm putting in and again when they are already written. From here, everything is perfectly fine and the data is what it's suppose to be. So, once again it's the way the data is sampled has to be off. So, I'm suspecting that the issue with my red missing and with multiple objects are related.

Just in case, here is my code for my Texture:


sceneTexture = new Texture(renderer.Device, 640, 640, 0, Usage.None, Format.R32F, Pool.Managed);


...when I write to the texture:


public void PreRender(Queue<CustomMesh> myMeshes)
{
unsafe
{
float* data = (float*)sceneTexture.LockRectangle(0, LockFlags.None).InternalData.ToPointer();

foreach (CustomMesh mesh in myMeshes)
{
*data = (float)mesh.Type; data++;
*data = mesh.X; data++;
*data = mesh.Y; data++;
*data = mesh.Z; data++;
*data = ((float)mesh.Color.R / 255.0f); data++;
*data = ((float)mesh.Color.G / 255.0f); data++;
*data = ((float)mesh.Color.B / 255.0f); data++;
*data = mesh.Diffuse; data++;
*data = mesh.Specular; data++;
*data = mesh.Reflection; data++;
*data = mesh.Radius2; data++;
}

sceneTexture.UnlockRectangle(0);
myTracer.SetValue("dynamicTotal", myMeshes.Count);
}
}


...and a the sampling code in the shader:


texture sceneData;
sampler2D sampleData = sampler_state
{
Texture = <sceneData>;
MinFilter = Point;
MagFilter = Point;
AddressU = Clamp;
AddressV = Clamp;
};

.....

const static float size = 640.0
const static float ObjType = 1.0 / size;
const static float PositionX = 2.0 / size;
const static float PositionY = 3.0 / size;
const static float PositionZ = 4.0 / size;
const static float ColorR = 5.0 / size;
const static float ColorG = 6.0 / size;
const static float ColorB = 7.0 / size;
const static float ObjDiffuse = 8.0 / size;
const static float ObjSpecular = 9.0 / size;
const static float Reflection = 10.0 / size;
const static float RadiusSquared = 11.0 / size;

.....

float normHitobj = ((float)hitobj / size);
float rU = normHitobj + ColorR;
float gU = normHitobj + ColorG;
float bU = normHitobj + ColorB;

float oRed = (tex2Dlod(sampleData, float4(rU, 0.0, 0.0, 0.0) )).r;
float oGreen = (tex2Dlod(sampleData, float4(gU, 0.0, 0.0, 0.0) )).r;
float oBlue = (tex2Dlod(sampleData, float4(bU, 0.0, 0.0, 0.0) )).r;

float4 objectColor = { oRed, oGreen, oBlue, 1.0 };


Man, I just wish I could deal with large arrays and not textures cause everything works with the smaller arrays but if I try to add around 10 or more objects using arrays to hold the data. Yes, it's slow slugglish it's impossible to do anything anymore. I hope someone here can point out anything I'm doing wrong here.

Share this post


Link to post
Share on other sites
Well, I was able to finally figure out my issue. It turned out it was 2 areas of floating point precision that I didn't take into consideration.

First error was that my variable for the object index was an int and I was casting it to a float for every time I used it within the equations. Changed this variable to a float and took out all the type casting. I should have been shot for this issue.

Second error was with my texture, since I was not dealing with a power-of-2 texture (640x640) apparently my data was not getting placed one after another. The data was getting stretch over to other areas of the texture for some reason. Not sure if this is true or not but I do know that everything seemed to be shifted a little at the begging and even more later on in the texture. Once I realized what was going on, I changed the texture to 512x512 to test this theory out and sure enough I was able to sample my texture and get the data I needed.

With that said I now have a fully working real time ray tracer tied to an editor that allows the user to build a scene and update each object.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!