Unraveling DX Studio internals

If you just want to read about the environment implementation, jump to The Cubemap.

Before going into real game making, I have been researching in to some of the capabilities of DX Studio, to know what it is really capable of.

I tried the Ocean plugin.

jpg

It gives you realtime reflections and some nice looking waves. But what if you need to use the reflections in a small pond or river. Is it possible? I found that there is no way to do it natively, it is, using some included function such as “set realtime reflection”. You, however, can set an object to use a static cubemap. But then, what is needed to use realtime mirror-like reflections?

Render to texture is my first thought. Quick search in Google reports very little examples using RTT in DirectX, mostly in DX11. I am a person who needs to see stuff working to understand it. If you tell me: “You can do X doing 1, then 2, then 3” I won’t be able to do it. I need to see the actual working code to see what does what. Then, if I understand it, change it to do what I need.

Without example code, I tried to figure it out by myself. First, is there any Render-To-Texture function in DX Studio? Yes. It is called RenderToResource. It works by taking a filename, width and height. Then, it renders the current camera of the top layer to a file in the resource folder. A pause here to talk about DX Studio Folder structure.

In DX Studio, I discovered that you can find three “levels” of file distribution.

  1. Windows file system. It is the one you can access through the normal paths, such as “c:\windows”. You can load textures and objects from there to DX Studio and save them back.
  2. DX Studio internal Resources file system. There are stored all the files you load in the editor and in runtime from the Windows file system. It is usually compressed and sometimes encrypted. And guess what, you can modify it runtime. You access it using the “#” at the beginning of the path.
  3. Memory files. There are the objects that your DX Studio document is actually using. Stuff gets there by specific commands such as scene.environmentAdd.

tree3

With this knowledge in mind, I wonder: How to render the scene and use it in a mirror?

First, set the camera.

I struggled a little with this, since you can’t use a unique camera for this scene rendering. You need two. And two layers. The first will be your RTT layer, using a camera positioned through code. But since DX Studio sends the input for automated camera movement to the top camera, you need to send the top layer to the bottom in the first render of your scene, so the second layer with your user-controlled camera ends on top. Maybe if you are handling the camera movement with your own code you don’t need to send the layer to the back, but still you will need to hide it somehow and, since you cannot set the layer visibility as false, nor the opacity =0, you probably will send it back anyway. Or you could make it 1,1 of size, maybe.

The position of the camera must be set using complex trigonometry function and sincerely I just can’t understand trigonometry. Simply I can’t see it. So I just attached the camera to the mirror and offset its position by the camera’s X and Z. Since the mirror was facing Z and the capture must be done from behind the mirror, I converted the Z pos to neg. Also, set the camera’s FOV to 90, so it sees the whole area it needs.

objects.camera_1.camera.fovVertical=90; //just set the vertical FOV. The horizontal is locked and you don’t need it.
objects.camera_1.offsetPosition=objects.camera_2.pos;
var c=objects.camera_1.offsetPosition;
c.z*=-1;

Second, render the scene.

scene.RenderToResource(“RTTscene.bmp”,100,100);

Somebody in DX Studio forum said that this function is too slow to effectively use it. But why? Very simple. I think he was saving the image as .jpg. Since jpg format is a compressed one, saving the file requires an extra step, the compression. So the most efficient way is to store as plain .bmp image. Also, by logic, the lower the resolution of your render, the faster the function will be. It is also useful to notice that the rendering works better if it is done on each update. You can do it with a timer, but I discovered that the time taken in rendering will cause a disgusting uneven jump effect if it is not constant, like when using a timer. So to avoid that effect and instead have a uniform slowdown, render the scene onUpdate.

Third, set the texture.

Problems. If you are using a simple plane, you may end up with inverted textures. To fix it, you can just rotate the plane until everything looks ok, or rotate the camera. Then, set the texture of your mirror plane.

objects.mirror.materials.Default.textureMap=”RTTscene.bmp”;

And with that, we ended with a beautiful misplaced reflection. Why? Turns out that you need to make additional calculations to properly project the texture onto the mirror.

refl

I then searched for code to do my mirror using C++ code and put everything in a plugin, but again, I found info on how to create planar reflections using  stencil mirrors, but no complete examples (with code) so I gave up. Still, this method can be adapted to render from the user’s perspective, enabling us to create post processing effects, such as bloom or others. Maybe I’ll try that later or maybe not, because it requires trigonometry again.

The Cubemap.

Wouldn’t be easier and more accurate to use a realtime cubemap? Is it possible? I read that realtime cubemaps are slow, but what the heck! Let’s try it ourselves.

In Unity, there is a function called Camera.RenderToCubemap. In DX Studio, there is no built-in way to render them. In DX Studio, cubemaps are called Environment maps. You can add them in realtime, but for the purpose of this method, I add them in the editor.

First, add a general environment. That one will be used as the standard background. Can be some hills, the cloudy ocean or whatever you want. After that, add your reflection environment. It is the one that will be overwritten in realtime with our rendered scene. This reflection environment must consist of six .bmp images, so the .xml announces them and we can replace them fast with our own images. I also set in the editor all the objects with reflection to have an “environment” as the reflection type. For mirror like surfaces, the Ambient, Emissive and Diffuse colors of the material must be black. The Gradient must be 0% and the Reflection must be 100%. Set the Specular values as you like.

Take note about the path of your reflection environment in the Resources tab in the editor. The images that need to be edited are those inside the environment file, so in my case, with an environment file called “test_env.dxenv” and a scene called “scene_1”, my path was:

var envpath="#scene_1.dxscene\\test_env.dxenv\\";

Inside are the six images that make the cubemap. Using their names, we render the scene and override each file with the new one. The FOV must be 90, the resolution must be square and the rotation must be set accordingly before each render. To get the most accurate results, the position of the camera should be in the center of the object that will use the cubemap.

objects.camera_1.pos= objects.mirror.pos;
var res = 100;
objects.camera_1.rot =new Rotation(0,90,0);
scene.renderToResource(envpath+"xPos.bmp", res, res) ;
objects.camera_1.rot =new Rotation(0,-90,0);
scene.renderToResource(envpath+"xNeg.bmp", res, res) ;
objects.camera_1.rot =new Rotation(-90,0,0);
scene.renderToResource(envpath+"yPos.bmp", res, res) ;
objects.camera_1.rot =new Rotation(90,0,0);
scene.renderToResource(envpath+"yNeg.bmp", res, res) ;
objects.camera_1.rot =new Rotation(0,0,0);
scene.renderToResource(envpath+"zPos.bmp", res, res) ;
objects.camera_1.rot =new Rotation(0,-180,0);
scene.renderToResource(envpath+"zNeg.bmp", res, res) ;

With this you are replacing the images inside the .dxenv file stored in the resources. Now, let’s move the changes into memory.

scene.environmentAdd("test_env", "#scene_1.dxscene\\test_env.dxenv");

This forces the environment map in memory to be reloaded from the resources folder. Finally, assign our modified environment to the objects with reflection, so they use the new cubemap instead of the one used as background of the scene.

objects.mirror.materials.Default.environment="test_env";

finalscene

Ready. If you have multiple reflective objects maybe you would like to have multiple cubemaps, so the reflections are more accurate for each of them. If the target computer can handle it, it might be entirely possible. Anyway, the cubemap works better in objects with irregular shapes that don’t require exact reflections, such as cars or spheres, so the usage may be limited.

On a final note, all this exercise was to create a mirror surface, but he resulting effect has almost the same perspective problems that a single shot render have. I found a page talking about that and providing solutions, so fixing the problems is possible, but not easy, so I won’t try it for now. If you want to read it, here is the link:

http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/

 That’s all. Enjoy the realtime cubemaps. I already know in what I’m going to use them 🙂

Download executable to test: http://d-h.st/ggL

Download .dxstudio file: http://d-h.st/cR2

(The files were removed due to lack of downloads and I no longer have the originals. Sorry)

Advertisements