Visualization LibraryA lightweight C++ OpenGL middleware for 2D/3D graphics |
[Home] [Tutorials] [All Classes] [Grouped Classes] |
"Texturing" is the technique used to apply in various ways an image over the surface of an object. Textures can be used to change or modify the color, the transparency and even the lighting properties of an object. In this tutorial we will go through the implementation of the texturing test ("src/examples/Applets/App_Texturing.hpp") part of the Visualization Library regression test suite. This will give us the chance to see how to use several standard and advanced texturing techniques like 2D texturing, multi-texturing, 3D textures, 1D and 2D texture arrays, sphere-mapping and cubemaps environment mapping. We will also see how to use the texture lod bias to simulate opaque reflections.
The most important classes involved in the texturing process are:
BaseDemo so the only thing we do is to derive from it, implement a set of functions testing the different texturing technique and reimplementing the virtual functions initEvent() and run() to initialize and animate our test.
class App_Texturing: public BaseDemo { public:
void multitexturing()
{
Create a box 5x5x5 and generate texture coordinates (generates only box->texCoordArray(0)).
Compute the normals for the box.
Share the same texture coordinates for texture unit #1 and #0. This is VERY important as for every texture unit we need a corresponding set of texture coordinates. In our case makeBox() automatically generated the texture coordinates for the texture unit #0 and we can simply 'recycle' them also for texture unit #1.
if (!GLEW_ARB_multitexture) { vl::Log::error("Multitexturing not supported.\n"); return; } vl::ref<vl::Geometry> box = vlut::makeBox( vl::vec3(0,0,0), 5,5,5, true ); box->computeNormals(); box->setTexCoordArray(1, box->texCoordArray(0));
Creates a vl::Transform for each cube so we can procedurally animate them and attaches them to the scene's root transform.
mCubeRightTransform = new vl::Transform;
mCubeLeftTransform = new vl::Transform;
vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(mCubeRightTransform.get());
vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild(mCubeLeftTransform.get());
First of all we need to load the images that we intend to use with our textures
vl::ref<vl::Image> img_holebox = vl::loadImage("/images/holebox.tif");
vl::ref<vl::Image> img_detail = vl::loadImage("/images/detail.tif");
Once we loaded our images we can create our textures from them. Here we use the GL_RGBA texture format, enable mipmapping and disable texture border. Note that even if the vl::Texture will be allocated and configured, the actual OpenGL texture object will not be created yet. Instead the OpenGL texture object will be created automatically during the rendering the first time it is used. Alternatively you can explicitly create it by calling
tex_holebox->createTexture()
Another way to create a 2D texture would be
vl::ref<vl::Texture> tex_holebox = new vl::Texture; tex_holebox->setupTexture2D(img_holebox.get(), vl::TF_RGBA, mMipmappingOn, false);
Note that upon creation of the OpenGL texture object VL will also automatically generate the mipmaps (if requested) using the hardware acceleration where available or using GLU.
vl::ref<vl::Texture> tex_holebox = new vl::Texture(img_holebox.get(), vl::TF_RGBA, mMipmappingOn, false);
vl::ref<vl::Texture> tex_detail = new vl::Texture(img_detail.get(), vl::TF_RGBA, mMipmappingOn, false);
Since we requested mipmapping we set the MinFilter to GL_LINEAR_MIPMAP_LINEAR, i.e. trilinear filtering. Note that while you can set the MinFilter to any of GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR, you can set the MagFilter only to GL_NEAREST, GL_LINEAR.
tex_holebox->getTexParameter()->setMagFilter(vl::TPF_LINEAR);
tex_holebox->getTexParameter()->setMinFilter(vl::TPF_LINEAR_MIPMAP_LINEAR);
tex_detail->getTexParameter()->setMagFilter(vl::TPF_LINEAR);
tex_detail->getTexParameter()->setMinFilter(vl::TPF_LINEAR_MIPMAP_LINEAR);
Here we are going to create 2 vl::Effect objects: cube_right_fx and cube_left_fx with the specific settings for the left and right cubes.
The right cube is going to be textured with one simple texture.
vl::ref<vl::Light> light = new vl::Light(0);
vl::ref<vl::Effect> cube_right_fx = new vl::Effect;
// to ensure the cubes are drawn after the textured quads
cube_right_fx->setRenderRank(1);
cube_right_fx->shader()->setRenderState( light.get() );
cube_right_fx->shader()->enable(vl::EN_LIGHTING);
cube_right_fx->shader()->enable(vl::EN_DEPTH_TEST);
cube_right_fx->shader()->enable(vl::EN_BLEND);
cube_right_fx->shader()->enable(vl::EN_ALPHA_TEST);
cube_right_fx->shader()->gocAlphaFunc()->set(vl::FU_GEQUAL, 1.0f - 0.02f);
cube_right_fx->shader()->gocLightModel()->setTwoSide(true);
cube_right_fx->shader()->gocTextureUnit(0)->setTexture( tex_holebox.get() );
The left cube is going to use multitexturing: base texture + detail texture.
The color of the detail texture will be used to modulate the color coming from lighiting and the base texture.
vl::ref<vl::Effect> cube_left_fx = new vl::Effect;
// to ensure the cubes are drawn after the textured quads
cube_left_fx->setRenderRank(1);
cube_left_fx->shader()->setRenderState( light.get() );
cube_left_fx->shader()->enable(vl::EN_LIGHTING);
cube_left_fx->shader()->enable(vl::EN_DEPTH_TEST);
cube_left_fx->shader()->enable(vl::EN_BLEND);
cube_left_fx->shader()->enable(vl::EN_ALPHA_TEST);
cube_left_fx->shader()->gocAlphaFunc()->set(vl::FU_GEQUAL, 1.0f - 0.02f);
cube_left_fx->shader()->gocLightModel()->setTwoSide(true);
cube_left_fx->shader()->gocTextureUnit(0)->setTexture( tex_holebox.get() );
cube_left_fx->shader()->gocTextureUnit(1)->setTexture( tex_detail.get() );
cube_left_fx->shader()->gocTexEnv(1)->setMode( vl::TEM_MODULATE );
Add the left and right cube to the scene.
sceneManager()->tree()->addActor( box.get(), cube_right_fx.get(), mCubeRightTransform.get() );
sceneManager()->tree()->addActor( box.get(), cube_left_fx.get(), mCubeLeftTransform.get() );
}
void texture3D()
{
As usual we start by loading our image with the difference that this time it is a 3D image and we also convert its format and type to IT_UNSIGNED_BYTE/IF_LUMINANCE
vl::ref<vl::Image> img_volume = vl::loadImage("/volume/VLTest.dat");
Now we create a white plane 10x10 units wide, oriented towards the viewer (without the rotation it would be on the x/z plane instead of being on the x/y plane).
Note 3 things:
vl::ref<vl::Geometry> quad_3d = vlut::makeGrid( vl::vec3(0,0,0), 10, 10, 2, 2 );
quad_3d->setColorArray(vlut::white);
quad_3d->transform( vl::mat4::rotation(90, 1,0,0), false );
Since we want to animate the texture coordinates of our plane we manually allocate a vl::ArrayFVec3 to be used as the texture coordinate array for the texture unit #0. We also disable the vertex buffer objects so that any change made locally to our buffer will be immediately visible. If VBO were enabled we would have had to update the VBOs after the animation, this way we make the things a bit less efficient but easyer to follow. The animation of the texture coordinates is done in the run() method (see the sources).
mTexCoords_3D = new vl::ArrayFVec3;
quad_3d->setTexCoordArray(0, mTexCoords_3D.get());
mTexCoords_3D->resize( 2*2 );
quad_3d->setVBOEnabled(false);
This is how the animation of the 3D texture coordinates looks like:
// 5 seconds period: t = 0 -> 1 float t = sin( (float)vl::Time::timerSeconds()*vlfPI*2.0f/5.0f) * 0.5f + 0.5f;
mTexCoords_3D->at(0) = vl::fvec3(0, 0, t); mTexCoords_3D->at(1) = vl::fvec3(0, 1, t); mTexCoords_3D->at(2) = vl::fvec3(1, 0, t); mTexCoords_3D->at(3) = vl::fvec3(1, 1, t);Since the 't' coordinate changes from 0 to 1 the 2d plane will show a series of 2d slices of the 3d texture as if it was a movie!
Now we create a vl::Effect which uses our 3D texture.
In order to use any 3D texture though we have to check that our OpenGL implementation actually supports 3D textures. The GLEW library embedded in Visualization Library lets us do this in a very simple and expressive manner as you can see. We also enable mipmapping as we did in the multi-texturing example. The function that actually creates our 3D texture is setupTexture3D().
vl::ref<vl::Effect> fx_3d = new vl::Effect;
if(GLEW_VERSION_1_2||GLEW_EXT_texture3D)
{
vl::ref<vl::Texture> texture_3d = new vl::Texture;
texture_3d->setupTexture3D( img_volume.get(), vl::TF_RGBA, mMipmappingOn, false );
fx_3d->shader()->gocTextureUnit(0)->setTexture( texture_3d.get() );
texture_3d->getTexParameter()->setMagFilter(vl::TPF_LINEAR);
texture_3d->getTexParameter()->setMinFilter(vl::TPF_LINEAR_MIPMAP_LINEAR);
}
else
vl::Log::error("Texture 3D not supported.\n");
At this point we have everything that is needed to create and add an Actor to the scene. We will position the plane on the top left corner <-6,+6,0>. Note that in this case we don't bind act_3d's Transform to any parent Transform as we will not animate it during the rendering. Instead we simply set the local matrix and compute manually the world matrix (which is the one used during the rendering).
vl::Actor* act_3d = sceneManager()->tree()->addActor( quad_3d.get(), fx_3d.get(), new vl::Transform );
act_3d->transform()->setLocalMatrix( vl::mat4::translation(-6,+6,0) );
act_3d->transform()->computeWorldMatrix();
}
void texture2DArray()
{
Using 2D texture arrays (GL_TEXTURE_2D_ARRAY) is very similar to using normal 3D textures (GL_TEXTURE_3D), but with the following differences:
GL_TEXTURE_1D/2D/3D textures), i.e. 1, 2, 3, 4, 5 etc. instead of 0.02, 0.04, 0.06, 0.08 etc. for example.For our demo we will use the 2D texture array in the very same way as we did for the 3D textures, but in this case we will put the textured plane on the top right corner. Note the use of the function setupTexture2DArray().
vl::ref<vl::Image> img_volume = vl::loadImage("/volume/VLTest.dat"); m2DArraySize = img_volume->depth(); // save this to be used during the animation vl::ref<vl::Geometry> quad_2darray = vlut::makeGrid( vl::vec3(0,0,0), 10, 10, 2, 2 ); quad_2darray->setColorArray(vlut::white); quad_2darray->transform( vl::mat4::rotation(90, 1,0,0), false ); mTexCoords_2DArray = new vl::ArrayFVec3; quad_2darray->setTexCoordArray(0, mTexCoords_2DArray.get()); mTexCoords_2DArray->resize( 2*2 ); quad_2darray->setVBOEnabled(false); vl::ref<vl::Effect> fx_2darray = new vl::Effect; if(GLEW_EXT_texture_array||GLEW_VERSION_3_0) { vl::ref<vl::Texture> texture_2darray = new vl::Texture; texture_2darray->setupTexture2DArray( img_volume.get(), vl::TF_RGBA, mMipmappingOn ); fx_2darray->shader()->gocTextureUnit(0)->setTexture( texture_2darray.get() ); texture_2darray->getTexParameter()->setMagFilter(vl::TPF_LINEAR); texture_2darray->getTexParameter()->setMinFilter(vl::TPF_LINEAR_MIPMAP_LINEAR); // we need an OpenGL Shading Language program that uses 'sampler2DArray()' to access the texture! vl::GLSLProgram* glsl = fx_2darray->shader()->gocGLSLProgram(); glsl->attachShader( new vl::GLSLFragmentShader("/glsl/texture_2d_array.fs") ); } else vl::Log::error("Texture 2d array not supported.\n"); vl::Actor* act_2darray = sceneManager()->tree()->addActor( quad_2darray.get(), fx_2darray.get(), new vl::Transform ); act_2darray->transform()->setLocalMatrix( vl::mat4::translation(+6,+6,0) ); act_2darray->transform()->computeWorldMatrix(); }
The fragment shader 'glsl/texture_2d_array.fs' used in the example looks like this:
#extension GL_EXT_texture_array: enable uniform sampler2DArray sampler0; void main(void) { gl_FragColor = texture2DArray(sampler0, gl_TexCoord[0].xyz ); }
Since the 2D texture arrays take integer coordinates we will animate them using 't*m2DArraySize' instead of simply 't' like this:
mTexCoords_2DArray->at(0) = vl::fvec3(0, 0, t*m2DArraySize);
mTexCoords_2DArray->at(1) = vl::fvec3(0, 1, t*m2DArraySize);
mTexCoords_2DArray->at(2) = vl::fvec3(1, 0, t*m2DArraySize);
mTexCoords_2DArray->at(3) = vl::fvec3(1, 1, t*m2DArraySize);
In this example we create again a plane oriented towards the views with the difference that this time instead of being a simple plane with 2*2=4 vertices we create 2*img_holebox->height() vertices, that is, we cut it in img_holebox->height() slices. Each slice will be textured using a 1D texture taken from the 1D texture array. The resulting image will look very similar to a 2D textured quad.
void texture1DArray() { vl::ref<vl::Image> img_holebox = vl::loadImage("/images/holebox.tif"); m1DArraySize = img_holebox->height(); // save this to be used during the animation // create a grid with img_holebox->height() slices vl::ref<vl::Geometry> quad_1darray = vlut::makeGrid( vl::vec3(0,0,0), 10, 10, 2, img_holebox->height() ); quad_1darray->setColorArray(vlut::white); quad_1darray->transform( vl::mat4::rotation(90, 1,0,0), false ); mTexCoords_1DArray = new vl::ArrayFVec2; quad_1darray->setTexCoordArray(0, mTexCoords_1DArray.get()); mTexCoords_1DArray->resize( 2 * img_holebox->height() ); quad_1darray->setVBOEnabled(false); vl::ref<vl::Effect> fx_1darray = new vl::Effect; if(GLEW_EXT_texture_array||GLEW_VERSION_3_0) { vl::ref<vl::Texture> texture_1darray = new vl::Texture; texture_1darray->setupTexture1DArray( img_holebox.get(), vl::TF_RGBA, mMipmappingOn ); fx_1darray->shader()->gocTextureUnit(0)->setTexture( texture_1darray.get() ); texture_1darray->getTexParameter()->setMagFilter(vl::TPF_LINEAR); texture_1darray->getTexParameter()->setMinFilter(vl::TPF_LINEAR_MIPMAP_LINEAR); // we need an OpenGL Shading Language program that uses 'sampler1DArray()' to access the texture! vl::GLSLProgram* glsl = fx_1darray->shader()->gocGLSLProgram(); glsl->attachShader( new vl::GLSLFragmentShader("/glsl/texture_1d_array.fs") ); } else vl::Log::error("Texture 1d array not supported.\n"); vl::Actor* act_1darray = sceneManager()->tree()->addActor( quad_1darray.get(), fx_1darray.get(), new vl::Transform ); act_1darray->transform()->setLocalMatrix( vl::mat4::translation(+6,-6,0) ); act_1darray->transform()->computeWorldMatrix(); }
The fragment shader 'glsl/texture_1d_array.fs' used in the example looks like this:
#extension GL_EXT_texture_array: enable uniform sampler1DArray sampler0; void main(void) { gl_FragColor = texture1DArray(sampler0, gl_TexCoord[0].xyz ); }
Inside the run() method we also animate the s/t texture coordinates of the plane in a more sofisticated way so that the slices of the plane become more apparent:
for(int i=0; i<m1DArraySize; ++i)
{
mTexCoords_1DArray->at(i*2+0) = vl::fvec2(0+t*0.02f*(i2?+1.0f:-1.0f), (float)i);
mTexCoords_1DArray->at(i*2+1) = vl::fvec2(1+t*0.02f*(i2?+1.0f:-1.0f), (float)i);
} The important part to be noted here is that here 'i', an integer coordinate, is used here instead of a normalized floating point number ranging between 0 and 1, as we would have done for 1D, 2D, 3D and cubemap textures.
GL_TEXTURE_RECTANGLE) is a special kind of 2D textures often used for post processing effects. They differ from normal 2D texture for the following:GL_CLAMP, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER.
void textureRectangle()
{
First we load our image in the usual way
vl::ref<vl::Image> img_holebox = vl::loadImage("/images/holebox.tif");
Now we create our plane to be textured as we did in the above examples with one major difference, we ask makeGrid() to generate for us 2d texture coordinates ranging from <0,0> to <img_holebox->width(),img_holebox->height()>.
// generate non-normalized uv coordinates, i.e. from <0,0> to <img_holebox->width(),img_holebox->height()>
vl::ref<vl::Geometry> quad_rect = vlut::makeGrid( vl::vec3(0,0,0), 10.0f, 10.0f, 2, 2, true, vl::fvec2(0,0), vl::fvec2((float)img_holebox->width(),(float)img_holebox->height()) );
quad_rect->setColorArray(vlut::white);
quad_rect->transform( vl::mat4::rotation(90, 1,0,0), false );
Now we create our texture rectangle using the function setupTextureRectangle(). Note that we disable mipmapping and set the clamping mode to vl::TPW_CLAMP (GL_CLAMP).
vl::ref<vl::Effect> fx_rect = new vl::Effect;
if(GLEW_ARB_texture_rectangle||GLEW_EXT_texture_rectangle||GLEW_NV_texture_rectangle/*TODO:||GLEW_VERSION_3_1*/)
{
vl::ref<vl::Texture> texture_rect = new vl::Texture;
texture_rect->setupTextureRectangle( img_holebox.get(), vl::TF_RGBA );
fx_rect->shader()->gocTextureUnit(0)->setTexture( texture_rect.get() );
// mipmaps not allowed with texture rectangle!
texture_rect->getTexParameter()->setMagFilter(vl::TPF_LINEAR);
texture_rect->getTexParameter()->setMinFilter(vl::TPF_LINEAR);
// GL_REPEAT (the default) not allowed with texture rectangle!
texture_rect->getTexParameter()->setWrapS(vl::TPW_CLAMP);
texture_rect->getTexParameter()->setWrapT(vl::TPW_CLAMP);
texture_rect->getTexParameter()->setWrapR(vl::TPW_CLAMP);
}
else
vl::Log::error("Texture rectangle not supported.\n");
vl::Actor* act_rect = sceneManager()->tree()->addActor( quad_rect.get(), fx_rect.get(), new vl::Transform );
act_rect->transform()->setLocalMatrix( vl::mat4::translation(-6,-6,0) );
act_rect->transform()->computeWorldMatrix();
}
Spherical mapping is very simple to use all we need is:
For more information about spherical mapping see also:
In our test we will apply spherical mapping to a rotating torus.
void sphericalMapping()
{
First of all we load the 2d texture containing our spherical map.
vl::ref<vl::Image> img_spheric = vl::loadImage("/images/spheremap_klimt.jpg");
Here we create the torus to which we will apply the spherical map. Note that in order to use glTexGen()/GL_SPHERE_MAP our model need to have normals.
vl::ref<vl::Geometry> torus = vlut::makeTorus(vl::vec3(), 8,3, 40,40);
// we need normals in order for GL_SPHERE_MAP to work correctly!
torus->computeNormals();
The Effect applied to the torus will have depht testing, back face culling and lighting. Note that since no transform is specified for the light this means that the light will follow the camera (ie. a headlight).
mFXSpheric = new vl::Effect;
mFXSpheric->shader()->enable(vl::EN_DEPTH_TEST);
mFXSpheric->shader()->enable(vl::EN_CULL_FACE);
mFXSpheric->shader()->enable(vl::EN_LIGHTING);
mFXSpheric->shader()->setRenderState( new vl::Light(0) );
// to ensure the torus is drawn after the textured quads
mFXSpheric->setRenderRank(1);
Now we apply the texture to the effect used by the torus and enable sphere-map automatic texture coordinate generation.
vl::ref<vl::Texture> texture_spheric = new vl::Texture;
texture_spheric->setupTexture2D( img_spheric.get(), vl::TF_RGBA, mMipmappingOn, false );
mFXSpheric->shader()->gocTextureUnit(0)->setTexture( texture_spheric.get() );
texture_spheric->getTexParameter()->setAnisotropy(16.0);
texture_spheric->getTexParameter()->setMagFilter(vl::TPF_LINEAR);
texture_spheric->getTexParameter()->setMinFilter(vl::TPF_LINEAR_MIPMAP_LINEAR);
// enable automatic texture generation for s,t
mFXSpheric->shader()->gocTexGen(0)->setGenModeS(vl::TGM_SPHERE_MAP);
mFXSpheric->shader()->gocTexGen(0)->setGenModeT(vl::TGM_SPHERE_MAP);
Finally we add the torus to the scene and its transform to the scene's root transform, so that it gets updated every frame.
mActSpheric = sceneManager()->tree()->addActor( torus.get(), mFXSpheric.get(), new vl::Transform );
vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild( mActSpheric->transform() );
}
Implementing cubemapping is also very simple with Visualization Library, all you need is:
void cubeMapping()
{
You can load cubemap images in many different ways. You can assemble them on the fly using the vl::loadAsCubemap() functions or you can load it directly from a DDS file with the vl::loadImage() function.
// cube mapping, see also http://developer.nvidia.com/object/cube_map_ogl_tutorial.html vl::ref<vl::Image> img_cubemap = vl::loadCubemap( "/images/cubemap/cubemap00.png", // (x+) right "/images/cubemap/cubemap01.png", // (x-) left "/images/cubemap/cubemap02.png", // (y+) top "/images/cubemap/cubemap03.png", // (y-) bottom "/images/cubemap/cubemap04.png", // (z+) back "/images/cubemap/cubemap05.png");// (z-) front
Create a torus and compute its normals.
vl::ref<vl::Geometry> torus = vlut::makeTorus( vl::vec3(), 8,3, 40,40 );
// we need normals in order for GL_REFLECTION_MAP to work correctly!
torus->computeNormals();
Setup a simple Effect like we did for the sphere mapping example.
mFXCubic = new vl::Effect;
mFXCubic->shader()->enable(vl::EN_DEPTH_TEST);
mFXCubic->shader()->enable(vl::EN_CULL_FACE);
mFXCubic->shader()->enable(vl::EN_LIGHTING);
mFXCubic->shader()->setRenderState( new vl::Light(0) );
// to ensure the torus is drawn after the textured quads
mFXCubic->setRenderRank(1);
Here we create the cubemap texture. Note that in order to use cubmaps we need to check that either the GL_ARB_texture_cube_map extension or OpenGL 1.3 is supported. Note that we use the GL_CLAMP_TO_EDGE mode here to minimize the seams of the cubemaps. This does not remove the seams totally. In order to have a cubemap without seams the cubemap must be properly generated and adjusted by the texture artist. Note that we use GL_REFLECTION_MAP texture generation mode for the S, T and R texture coordinates. Remember that this texture generation mode works properly only when the model has the normals, as we already discussed. Note also the line:
mFXCubic->shader()->gocTextureMatrix(0)->setUseCameraRotationInverse(true);This tells VL to put in the texture matrix the inverse of the camera rotation. This puts into world-space the cubemap texture coordinates automatically generated by OpenGL, which would otherwise be in eye-space (ie. always facing the camera).
if (GLEW_VERSION_1_3||GLEW_ARB_texture_cube_map)
{
vl::ref<vl::Texture> texture_cubic = new vl::Texture;
texture_cubic->setupTextureCubemap( img_cubemap.get(), vl::TF_RGBA, mMipmappingOn, false );
mFXCubic->shader()->gocTextureUnit(0)->setTexture( texture_cubic.get() );
texture_cubic->getTexParameter()->setAnisotropy(16.0);
texture_cubic->getTexParameter()->setMagFilter(vl::TPF_LINEAR);
texture_cubic->getTexParameter()->setMinFilter(vl::TPF_LINEAR_MIPMAP_LINEAR);
texture_cubic->getTexParameter()->setWrapS(vl::TPW_CLAMP_TO_EDGE);
texture_cubic->getTexParameter()->setWrapT(vl::TPW_CLAMP_TO_EDGE);
texture_cubic->getTexParameter()->setWrapR(vl::TPW_CLAMP_TO_EDGE);
// enable automatic texture generation for s,t,r
mFXCubic->shader()->gocTexGen(0)->setGenModeS(vl::TGM_REFLECTION_MAP);
mFXCubic->shader()->gocTexGen(0)->setGenModeT(vl::TGM_REFLECTION_MAP);
mFXCubic->shader()->gocTexGen(0)->setGenModeR(vl::TGM_REFLECTION_MAP);
// texture matrix
mFXCubic->shader()->gocTextureMatrix(0)->setUseCameraRotationInverse(true);
}
else
vl::Log::error("Texture cubemap not supported.\n");
Finally we add the torus to the scene and its transform to the scene's root transform, so that it gets updated every frame.
mActCubic = sceneManager()->tree()->addActor( torus.get(), mFXCubic.get(), new vl::Transform );
vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()->addChild( mActCubic->transform() );
}
void run() { // rotating cubes if (GLEW_ARB_multitexture) { mCubeRightTransform->setLocalMatrix( vl::mat4::translation(+6,0,0) * vl::mat4::rotation( vl::Time::currentTime()*45, 0, 1, 0) ); mCubeLeftTransform ->setLocalMatrix( vl::mat4::translation(-6,0,0) * vl::mat4::rotation( vl::Time::currentTime()*45, 0, 1, 0) ); } // 5 seconds period float t = sin( (float)vl::Time::currentTime()*vl::fPi*2.0f/5.0f) * 0.5f + 0.5f; t = t * (1.0f - 0.02f*2) + 0.02f; // 3d texture coordinates animation mTexCoords_3D->at(0) = vl::fvec3(0, 0, t); mTexCoords_3D->at(1) = vl::fvec3(0, 1, t); mTexCoords_3D->at(2) = vl::fvec3(1, 0, t); mTexCoords_3D->at(3) = vl::fvec3(1, 1, t); // 2d texture array coordinates animation mTexCoords_2DArray->at(0) = vl::fvec3(0, 0, t*m2DArraySize); mTexCoords_2DArray->at(1) = vl::fvec3(0, 1, t*m2DArraySize); mTexCoords_2DArray->at(2) = vl::fvec3(1, 0, t*m2DArraySize); mTexCoords_2DArray->at(3) = vl::fvec3(1, 1, t*m2DArraySize); // 1d texture array coordinates animation for(int i=0; i<m1DArraySize; ++i) { mTexCoords_1DArray->at(i*2+0) = vl::fvec2(0+t*0.02f*(i%2?+1.0f:-1.0f), (float)i); mTexCoords_1DArray->at(i*2+1) = vl::fvec2(1+t*0.02f*(i%2?+1.0f:-1.0f), (float)i); } // spherical mapped torus rotation mActSpheric->transform()->setLocalMatrix( vl::mat4::translation(0,+6,0)*vl::mat4::rotation(45*vl::Time::currentTime(),1,0,0) ); mActSpheric->transform()->computeWorldMatrix(); // cube mapped torus rotation mActCubic->transform()->setLocalMatrix( vl::mat4::translation(0,-6,0)*vl::mat4::rotation(45*vl::Time::currentTime(),1,0,0) ); mActCubic->transform()->computeWorldMatrix(); }
virtual void mouseWheelEvent(int w) { mLodBias += w*0.3f; mLodBias = vl::clamp(mLodBias, 0.0f, 4.0f); mFXSpheric->shader()->gocTexEnv(0)->setLodBias(mLodBias); mFXCubic->shader()->gocTexEnv(0)->setLodBias(mLodBias); }
virtual void initEvent() { BaseDemo::initEvent(); trackball()->setTransform(vl::VisualizationLibrary::rendering()->as<vl::Rendering>()->transform()); mMipmappingOn = true; mLodBias = 0.0; multitexturing(); textureRectangle(); texture3D(); texture2DArray(); texture1DArray(); sphericalMapping(); cubeMapping(); } protected: vl::ref<vl::Transform> mCubeRightTransform; vl::ref<vl::Transform> mCubeLeftTransform; vl::ref<vl::ArrayFVec3> mTexCoords_3D; vl::ref<vl::ArrayFVec3> mTexCoords_2DArray; vl::ref<vl::ArrayFVec2> mTexCoords_1DArray; int m1DArraySize; int m2DArraySize; vl::Actor* mActSpheric; vl::Actor* mActCubic; vl::ref<vl::Effect> mFXSpheric; vl::ref<vl::Effect> mFXCubic; float mLodBias; bool mMipmappingOn; };