XNA 4.0 - Environment Mapping
Código fuente y ejecutable de la solución
Frustrado porque el libro que pensaba seguir se basa en DirectX 10 (que XNA no soporta), probablemente me pase a SlimDX. De todas formas, dejo mis experiencias y comparación entre los tipos de environment mapping en Directx9.
Sphere Mapping
La interpolación lineal hace desastres sobre la textura. Utilizando un modelo y meshes más grandes (como sponza.x), la solución de no renderizar las primitivas que pasan por la singularidad no sirvió. El ejemplo de D3DBook usa una interpolación de mayor orden (por lo que pude ver, interpolación baricéntrica de Lagrange). Para eso, se aprovecha de la posibilidad de trabajar al nivel de per primitive en el Geometry Shader y así tener acceso a las 3 normales del triángulo a la vez.
float3 HighOrderInterpolate( float3 normals[6], float x, float y )
{
float p0 = 2*x*x + 2*y*y + 4*x*y - 3*x - 3*y + 1;
float p1 = -4*x*x - 4*x*y + 4*x;
float p2 = 2*x*x - x;
float p3 = -4*y*y - 4*x*y + 4*y;
float p4 = 2*y*y - y;
float p5 = 4*x*y;
return p0*normals[0] + p1*normals[1] + p2*normals[2] +
p3*normals[3] + p4*normals[4] + p5*normals[5];
}
Dual Paraboloid Mapping
Otra vez el problema de interpolación. En este caso resulta en una división entre ambos paraboloides y stretching de algunos triángulos. El ejemplo, en cambio, sólo tiene una pequeña línea negra.
Cube Mapping
En este caso, no hay pérdida de calidad visual por usar Dx9, sino de performance. Al no poder usar un array de rendertargets y setear el index en el shader, se deben realizar 6 pasadas (enviando a renderizar 6 veces cada primitiva).
Mejoras a la solución
Adapté la opción de mostrar passes y sacar screenshots para que soporte cubemaps e hice un shader que renderiza las 6 caras a una Texture2D (para que las 6 caras se consideren una pasada y se muestren como un cubo desenvuelto). En la carpeta Content de la solución, aparte del .fx que utiliza XNA, está el .rfx.
Implementé frustum culling usando BoundingSpheres (XNA no genera automáticamente BoundingBoxes) y un contador de vértices renderizados. Esto, junto con el indicador de FPSs, permite ver claramente la diferencia a nivel procesamiento de cada técnica (~300.000 de sphere vs ~1.100.000 de cube).
RenderTargetUsage
Esto me tuvo luchando por más de una hora hasta que pude detectar cuál era el problema. Acostumbrado a RenderMonkey, pensaba que el usage por defecto del backbuffer y los rendertargets era PreserveContents. En cambio, es DiscardContents, por lo que al renderizar el skybox en el backbuffer, después pasar al environment mapping y por último renderizar la escena, el sky se perdía.
Solución, setear el usage al crear el rendertarget o en el evento de preparación del GraphicsDevice:
rtScreenshot = new RenderTarget2D(GraphicsDevice, iWidth, iHeight, true, SurfaceFormat.Color, DepthFormat.Depth16, 0, RenderTargetUsage.PreserveContents);
graphics = new GraphicsDeviceManager(this);
graphics.PreparingDeviceSettings += OnPreparingDeviceSettings;
private void OnPreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
e.GraphicsDeviceInformation.PresentationParameters.RenderTarget
Usage = RenderTargetUsage.PreserveContents;
}
Video










