Original Post
Hi all I am writing this post because I implemented Deferred Rendering using World Space coordinates. I must admit that I think better in World Space than in other spaces, and this will explain why I have some lack of precision in my Deferred Rendering Technique. I want to show you how I done it and hopes if anyone can lead me to the correct changes to make all calculations in other space like eye or screen space. Deferred Rendering Technique in World Space: -------------------------------------------- First of all, I will shoy my distribution on 4 Render Targets to store data (also called G-Buffer). Render Target 1: Albedo.r Albedo.g Albedo.b NeedIlumination Render Target 2: Normal.r Normal.g Normal.b Depth Render Target 3: SpecularPower Specular Exponent Render Target 4: LightAccum.r LightAccum.g LightAccum.b SpecularAccum All these Render Targets are 16 bits per component, this let me compute HDR on some components and let me also to compute a glow from this data for later addition. * The first Render Target is inited with albedo (texture color) of object, also, if object doesn’t need illumination (like sky) it puts a 0.0f value on ‘NeedIllumination’ channel. Of course if it needs illumination, then it puts a 1.0f. This allows me to have objects with and without illumination in the same scene. * In the second Render Target, I fill the free three channels with Normal information. These Normals are stored in World Space coordinates, also they are stored with ((N+1) *0.5f) method. In the ‘Depth’ channel, I store (i.Distance - eye_near) / (eye_far - eye_near), being i.Distance the distance between eye_pos (the world position of the camera) and pixel_pos (the world position of the pixel). * The third Render Target is filled with specular information. For every object, I store his specular power (range from 0.0f to 1.0f) and his specular exponent (range from 1.0f to 100.0f multiplied by 0.01f to fit in channel). Of course, later I will multiply it by 100.0f to get again the exponent in correct conditions. * The fourth and last Render Target is used for lighting accumulation, but instead of filling with 0.0f values, I initialized with Lightmaps per object information and let ‘SpeccularAccum’ channel with 0.0f for later use. Now that I explained the Render Targets channels, I will explain how I get all the data in World Space coordinates. Vertex Shader: -------------- For geometry itself (vertex data) is not a problem, I multiply every vertex in vertex shader by matrix that contains: World Matrix, View Matrix, Projection Matrix, and the custom transformation of this object, I mean, rotations, translations, scalations, etc. A little more work comes with normals of the object. You need to rotate normals, but not to translate them nor scale, so I need to multiply the objects normals with the rotation matrix of the object (again, only rotate). With this, I have normals in a correct orientation, then, I need to initialize Tangent and Binormal vectors per vertex. So in vertex shader, I multiply Tangent vector (coming from vertex buffer) by Rotation matrix, and the same for Binormal vector. With these 3 vectors, Normal, Tangent and Binormal, we have the matrix called TBN that let us to transform a simple normal from our Normal map to a World Space coordinates normal. So I pass Normal, Tangent and Binormal to pixel shader. Pixel Shader: ------------- In pixel shader, I have the interpolated data of Normal, Tangent, Binormal and pixel world position for every pixel. With these, and some other data referent to texture coordinates, I can calculate all four Render Targets and of course fill them. For example, I multiply normal coming from Normal map by TBN matrix to get these normal in World Space, also I use this World Space Normal to calculate reflection color of surface if I need it. More calculations are made, but all are related to color, specular, etc. If you want to know more, please ask for more information :) Ok, at this point, all is ok; the problems arise in the next step. Light Accumulation Step: ------------------------ In this step, we have 4 Render Targets filled with data, no geometry and no more information than these 4 Render Targets, well… we have the information of the light we want to accumulate of course :) If I want to properly calculate illumination of any pixel, I need to have his World Position, and, for this, I only have his Depth from eye_pos. The method I use to recalculate the world position I this: I know eye_pos position in World Space, and I know his Depth, this Depth can be transformed to World Space multiplying it by Near_plane and Far_plane information, keep in mind that Depth is stored in 0.0f-1.0f range, so if you multiply it by Far_plane – Near_plane, you will have a World Space Depth from eye_pos. I need to know the direction of this Depth, because I need a point (eye_pos) and a vector (pixel_pos – eye_pos) to know another point (pixel_pos). To get the direction of every pixel on the screen, I use the near_plane corners, I mean, for rendering light accumulation stage, I render a plane, this plane is composed from 2 triangles, and 4 vertex. In every vertex of this plane, I put apart from position, the position of one corner of Near_plane, this position is in World Space, and the method to get these coordinates is simple. You only need to multiply [-1,1,0], [1,1,0], [-1,-1,0], [1,-1,0] by the inverse of ViewProjection matrix, and you will have these coordinates in World Space, in other words, these four coordinates will transform into four coordinates of the Near_plane. If we pass these coordinates from vertex shader to pixel shader, graphic card will interpolate for us, and we will have a point in Near_plane, with this point and eye_pos, we can calculate a direction vector. With this direction vector, depth, and eye_pos, all in World Space, we can get pixel_pos in World Space. But now, nightmares appear. Depth value is a value stored in a 16 bit channel, and this implies less precision than a 32 bits channel. In practice, I get problems of precision calculating lights data in coordinates that are far from [0.0f, 0.0f, 0.0f] in the scene. So I’m thinking that I will need to store Depth in a 32 bit channel, but this reduce my channels from 4 to 2, I mean, from 16x16x16x16 to 32x32, all the Render Targets must be the same size, in this case, 64 bits per Render Target. When you have the World Position of the pixel, the Light Accumulation is simple, only calculate the lights value for every pixel and add this to the same Render Target with blend activated, this will add every color you write. When you have all the lights added over the lightmaps initialization data, you can multiply these valors with Albedo and you will have an illuminated scene. At this point, I understand that in World Space, I need to use a 32 bits channel to store Depth, and of course, I lose channels, all the other people on the world is using 16 bits channel because they are making all the calculations in some kind of eye/screen space. So my question is: What I need to do to make my calculations eye/screen space friendly? How can you have reflections without a World Space normal to reflect from? And how to get normals in eye/screen space? Thanks for reading all this text and try to help me :) LLORENS