ands / lightmapper

A C/C++ single-file library for drop-in lightmap baking. Just use your existing OpenGL renderer to bounce light!
1.41k stars 133 forks source link

Can I decide which lightmap gets baked? #22

Closed Raildex closed 1 year ago

Raildex commented 1 year ago

I have several meshes. Each mesh has it's own unique lightmap. I want to bake lighting for each mesh separately, but shadows of other meshes should be visible on the currently baked lightmap.

Can lightmapper do this?

ands commented 1 year ago

Hey @Raildex,

yes, you can do this with lightmapper.

As the readme states, this is an example core loop to bake lightmaps:

for (int b = 0; b < bounces; b++)
{
    // render all geometry to their lightmaps
    for (int i = 0; i < meshes; i++)
    {
        memset(mesh[i].lightmap, 0, sizeof(float) * mesh[i].lightmapWidth * mesh[i].lightmapHeight * 3); // clear lightmap to black
        lmSetTargetLightmap(ctx, mesh[i].lightmap, mesh[i].lightmapWidth, mesh[i].lightmapHeight, 3);

        lmSetGeometry(ctx, mesh[i].modelMatrix,
            LM_FLOAT, (uint8_t*)mesh[i].vertices + positionOffset, vertexSize,
            LM_NONE , NULL, 0, // optional vertex normals for smooth surfaces
            LM_FLOAT, (uint8_t*)mesh[i].vertices + lightmapUVOffset, vertexSize,
            mesh[i].indexCount, LM_UNSIGNED_SHORT, mesh[i].indices);

        int vp[4];
        mat4 view, proj;
        while (lmBegin(&ctx, vp, &view[0][0], &proj[0][0]))
        {
            // don't glClear on your own here!
            glViewport(vp[0], vp[1], vp[2], vp[3]);
            drawScene(view, proj);
            printf("\r%6.2f%%", lmProgress(ctx) * 100.0f); // don't actually call printf that often ;) it's slow.
            lmEnd(&ctx);
        }
    }

    // postprocess and upload all lightmaps to the GPU now to use them for indirect lighting during the next bounce.
    for (int i = 0; i < meshes; i++)
    {
        // you can also call other lmImage* here to postprocess the lightmap.
        lmImageDilate(mesh[i].lightmap, temp, mesh[i].lightmapWidth, mesh[i].lightmapHeight, 3);
        lmImageDilate(temp, mesh[i].lightmap, mesh[i].lightmapWidth, mesh[i].lightmapHeight, 3);

        glBindTexture(GL_TEXTURE_2D, mesh[i].lightmapHandle);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mesh[i].lightmapWidth, mesh[i].lightmapHeight, 0, GL_RGB, GL_FLOAT, data);
    }
}

Let's ignore the bounces loop and the postprocessing for now and assume we only want one single light bounce.

With lmSetTargetLightmap and lmSetGeometry you set what you want to bake to (the output mesh & lightmap). So you decide which lightmaps and geometry you want to bake/update.

You also have full control of the input (e.g. lights/emissives and shadow casters) by what you decide to render between lmBegin and lmEnd.

For better understanding of what is happening, the loop while (lmBegin(&ctx, vp, &view[0][0], &proj[0][0])) { ... } will simply traverse the surface of your mesh that you set by lmSetGeometry. At each point on the mesh, it takes a sample of the environment by rendering a hemisphere/hemicube of the scene around it. The resulting scene render at that position is then integrated down to one single pixel value, which is then written out to the lightmap at the corresponding UV coordinates.

I hope this helps :)

Raildex commented 1 year ago

Thanks, that explains a lot :)