LightsprintSDK 2021.08.08
Environment map

Environment map (cube texture) is designed for global illumination of single object.

Suitable for

  • static objects: YES
  • dynamic objects: YES
  • realtime calculated illumination: YES
  • precalculated illumination: YES

Advantages

  • offers global illumination with both specular and diffuse reflections
  • object doesn't have to be part of static scene, no RRObject adapter is required
  • calculation is independent to object complexity, quick even for extremely complex objects

Disadvantages

  • precision decreases with size of object, suitable for characters and items, not for buildings
  • complexity of objects doesn't matter, but count of environment map updates does, so if you need large clouds/crowds of dynamic objects visible all at once, share one environment map for several close objects and update it only once to save time

Interface, implementations

Instances

Rendering

  • Many rendering techniques are based on faked precomputed environment maps. Here you get realtime computed environment maps, and you are free to use them for any purpose.
  • Request environment (cube) map to be generated in center of your object. Environment map may be later used by GPU to add global illumination to diffuse and specular surfaces close to given point in space.
  • We propose you several techniques:
    For rough surface with mostly diffuse reflection, read value from environment map's small mip level, using 'surface normal' as a coordinate. This single texture lookup gives you global illumination of pixel. Multiply it by material diffuse color to get final color.
    For smooth surface with specular reflection, read value from environment map, using 'eye direction reflected by surface' as a coordinate. Use smaller or larger mip level based on material shininess. This single texture lookup gives you global illumination of pixel. Don't modulate it by material color unless you want to render exotic materials, you already have final color. Size 16 of environment map is often good compromise between environment update speed and reflection sharpness.
    You can use normal map or height map to modulate surface normal. Both diffuse and specular surfaces respond well to bump maps.
    LightsprintGL implements all of these techniques.
  • Global illumination can be further improved if you use ambient occlusion map for your dynamic object. Multiply global illumination read from environment map by ambient occlusion read from ambient occlusion map to get more precise result. Ambient occlusion maps are computed for example by BuildLightmaps sample.

Examples

  • OpenGL example:
    Rendering with environment maps.
    rr::RRSolver* solver;
    GLuint program;
    ...
    // update environment maps
    solver->updateEnvironmentMap(illumination);
    // set program created from shader below
    glUseProgram(program);
    // bind diffuse environment map to texture0
    // it calls glBindTexture(GL_TEXTURE_2D,map);
    glActiveTexture(GL_TEXTURE0);
    rr_gl::getTexture(illumination->diffuseEnvMap)->bindTexture();
    // set sampler to use texture0
    glUniform1i(glGetUniformLocation(program,"diffuseEnvironmentMap"),0);
    // bind specular environment map to texture1
    glActiveTexture(GL_TEXTURE1);
    rr_gl::getTexture(illumination->specularEnvMap)->bindTexture();
    // set sampler to use texture1
    glUniform1i(glGetUniformLocation(program,"specularEnvironmentMap"),1);
    // render primitives
    glDrawElements...
    Data structure with object's illumination and more.
    Definition RRIllumination.h:40
    Global illumination solver for interactive applications.
    Definition RRSolver.h:58
    void bindTexture() const
    Binds texture.
    RR_GL_API Texture * getTexture(const rr::RRBuffer *buffer, bool buildMipMaps=true, bool compress=true, int magn=GL_LINEAR, int mini=GL_LINEAR, int wrapS=GL_REPEAT, int wrapT=GL_REPEAT)
    Converts rr::RRBuffer to Texture so it can be immediately used as a texture in OpenGL.
    Applying environment maps in GLSL fragment shader:
    uniform samplerCube specularEnvironmentMap;
    uniform samplerCube diffuseEnvironmentMap;
    void fragmentShader()
    {
    // normal in world space, you may apply normal map here
    vec3 worldNormal = ...;
    // view vector in world space = position of fragment - position of camera
    vec3 worldView = ...;
    // reflected view vector in world space
    vec3 worldViewReflected = reflect(worldView,worldNormal);
    ...
    gl_FragColor = ...
    // diffuse reflection
    + materialDiffuseReflectance *
    textureCube(diffuseEnvironmentMap, worldNormal)
    // specular reflection
    + materialSpecularReflectance *
    textureCube(specularEnvironmentMap, worldViewReflected);
    }
  • Direct3D 9 example:
    Rendering with environment maps.
    IDirect3DDevice9* device;
    IDirect3DPixelShader9* pixelShader;
    rr::RRSolver* solver;
    ...
    // update environment maps
    solver->updateEnvironmentMap(illumination);
    // set rendering pipeline to use shader below
    device->SetPixelShader(pixelShader);
    // set samplers to use environment maps
    ...
    // render primitives
    device->DrawPrimitive...
    Applying environment maps in HLSL pixel shader:
    samplerCUBE specularEnvironmentMap;
    samplerCUBE diffuseEnvironmentMap;
    void pixelShader(..., out float4 oColor: COLOR)
    {
    // normal in world space, you may apply normal map here
    float3 worldNormal = ...;
    // view vector in world space = position of fragment - position of camera
    float3 worldView = ...;
    // reflected view vector in world space
    float3 worldViewReflected = reflect(worldView,worldNormal);
    ...
    oColor = ...
    // diffuse reflection
    + materialDiffuseReflectance *
    texCUBE(diffuseEnvironmentMap, worldNormal)
    // specular reflection
    + materialSpecularReflectance *
    texCUBE(specularEnvironmentMap, worldViewReflected);
    }
  • See Direct3D, OpenGL or your engine documentation for more details on texturemapping and applying environment maps.