My Three.cpp library now supports shadow mapping

Standard

My ongoing port of three.js to three.cpp now supports shadow mapping / shadow rendering.

If you’ve been following my progress of porting three.js, now my three.cpp supports shadow mapping / shadow rendering. To enable shadow is fairly simple, you just need to toggle the flags on the mesh.

mesh->castShadow = true;   /* To cast shadow */
mesh->receiveShadow = true; /* To receive shadow from other objects */

I found that porting three.js to c++ can be full of traps, since Javascript is of dynamic typing, a number of data members can be declared impromptu in any part of the code. This shadow mapping actually utilises some of the code from my older OpenGL shadow mapping examples, thus maybe this is the point where three.cpp actually starts to diverge from three.js. Nevertheless, shadow mapping is now enabled, it supports PCF shadow.

Here’s a sample video clip of shadow mapping using my three.cpp

Here’s the complete code to render the example video above.

const string path = "/Users/saburookita/Personal Projects/Three.cpp Rev.2/examples/assets/";

        Renderer renderer;
        renderer.init( "Ex 004: Shadow Mapping", 1600 * 2 / 4, 900 * 2 / 4 );

        /* Create scene */
        auto scene = Scene::create();
        scene->setFog(Fog::create( 0x72645b / 2, 2.0, 15.0 ));
        scene->setViewport( 0.0, 0.0, renderer.getWidth(), renderer.getHeight() );
        scene->setShadowMapType( SHADOW_MAP::PCF_SOFT );

        /* Create camera */
        auto camera = PerspectiveCamera::create( 50.0, renderer.aspectRatio, 0.001, 100.0 );
        camera->translate(0.0, 1.5, 5.5);
        camera->lookAt( 0.0, 0.0, 0.0 );

        /* Create our objects */
        auto sphere = Mesh::create( SphereGeometry::create(30, 20, 0.66f ),
                                   PhongMaterial::create(0x777777, 0x0, 0x0, 0x999999, 30, true) );

        sphere->normalMap = TextureUtils::loadAsNormalMap  ( path, "tutorial_normals07.gif" );
        sphere->translate(0.0, 0.0, 0.0);
        sphere->castShadow = true;
        sphere->receiveShadow = true;

        auto cube = Mesh::create( CubeGeometry::create(1.0),
                                 PhongMaterial::create(0x777777, 0x0, 0x0, 0x0, 30, false) );
        cube->texture = TextureUtils::loadAsTexture( path, "four_shapes_color.tga" );
        cube->translate(-2.0, 0.0, -2.0);
        cube->castShadow = true;
        cube->receiveShadow = true;

        auto cylinder = Mesh::create( CylinderGeometry::create(0.5, 0.5, 1.0, 30, 5, true),
                                     PhongMaterial::create( 0xCCCCCC, 0x0, 0x0, 0x111111, 150.0, false ) );
        cylinder->material->side = SIDE::DOUBLE_SIDE;
        cylinder->castShadow = true;
        cylinder->receiveShadow = true;
        cylinder->texture   = TextureUtils::loadAsTexture   ( path, "rock_color.tga" );
        cylinder->normalMap = TextureUtils::loadAsNormalMap ( path, "rock_normal.tga" );
        cylinder->translate(+2.0f, 0.0f, -2.0f);

        scene->add( cylinder );
        scene->add( cube );
        scene->add( sphere );

        /* And the ground plane */
        auto plane = Mesh::create( PlaneGeometry::create(20.0f),
                                  PhongMaterial::create(0x777777, 0x777777, 0x0, 0x999999, 30) );
        plane->name = "plane";
        plane->rotateX(-90.0f);
        plane->translate(0.0, -1.0, 0.0);
        plane->receiveShadow = true;
        scene->add( plane );

        /* Cubemap */
        auto env = Mesh::create( CubeGeometry::create(20.0f), MeshCubeMapMaterial::create() );
        env->texture = TextureUtils::loadAsEnvMap( path + "cube/pisa",
                                                  "nx.png", "ny.png", "nz.png",
                                                  "px.png", "py.png", "pz.png");

        sphere->envMap = downcast(env->texture, EnvMap);
        scene->add( env );

        /* Create a (rotating) directional light */
        auto dir_light = DirectionalLight::create(0x99CCFF, 1.35, glm::vec3( 3.0, 1.0, 3.0 ) );
        dir_light->castShadow       = true;
        dir_light->shadowBias       = -0.05;
        dir_light->shadowMapSize    = glm::vec2(1024);
        scene->add( dir_light );

        /* Create a spotlight, the shadow should be casted no the left hand side */
        auto spot_light = SpotLight::create(0x99CCFF, 1.0, 20.0, 50.0, 1.0 );
        spot_light->position   = glm::vec3(3.0, 2.0, 3.0);
        spot_light->castShadow = true;
        scene->add( spot_light );

        /* Create an ambient light */
        scene->add( AmbientLight::create(0x777777));

        /* Create a post render callback for objects rotation */
        bool rotate_objects = false;
        float light_rotation_1 = 0.0;
        renderer.setPostRenderCallbackHandler( [&](){
            dir_light->position.x = 3.0 * cosf( light_rotation_1 );
            dir_light->position.z = 3.0 * sinf( light_rotation_1 );

            light_rotation_1 += 0.01;

            if( rotate_objects ) {
                cube->rotateX(-1.0f);
                cylinder->rotateX(1.0f);
            }
        });

        /* Override key callback handler */
        renderer.setKeyCallbackHandler([&](GLFWwindow *window, int key, int scancode, int action, int mod) {
            if( action == GLFW_PRESS ) {
                switch ( key) {
                    case GLFW_KEY_ESCAPE: case GLFW_KEY_Q:
                        glfwSetWindowShouldClose( window, GL_TRUE );
                        return;

                    case GLFW_KEY_R: /* Toggle rotation */
                        rotate_objects = !rotate_objects;
                        break;

                    default:
                        break;
                }
            }
        });

        renderer.gammaInput  = true;
        renderer.gammaOutput = true;
        renderer.clearColor = scene->getFog()->color;
        renderer.render(scene, camera );

Still, I can’t emphasize enough that the entire goal of this port is to have a 3D engine as simple as three.js. In the given example code, you can see that the entire creation of scene, objects, lights, are pretty simple and straightforward.

There’s still many room for improvements, from increasing the shadow quality, bug fixing, to implementing cascade shadows. However the framework is already there, thus adding extra functionalities should be straightforward.

If you’re interested with three.cpp, please visit my github repository at https://github.com/subokita/Three.cpp-Rev-2

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s