CS116b/CS216
Chris Pollett
Feb 10, 2014
static void keyboard(const unsigned char key, const int x, const int y) { switch (key) { case 27: //ESC exit(0); case 'h': cout << " ============== H E L P ==============\n\n" << "h\t\thelp menu\n" << "s\t\tsave screenshot\n" << "t\t\ttoggle between shaders\n" << "drag left mouse to rotate\n" << endl; break; case 's': glFlush(); writePpmScreenshot(g_windowWidth, g_windowHeight, "out.ppm"); break; case 't': g_activeShader++; if(g_activeShader >= g_numShaders) { g_activeShader = 0; } break; } glutPostRedisplay(); }
static const int g_numShaders = 3; static const char * const g_shaderFiles[g_numShaders][3] = { {"./shaders/basic-gl3.vshader", "./shaders/texture-gl3.fshader"}, {"./shaders/basic-gl3.vshader", "./shaders/normal-gl3.fshader"}, {"./shaders/basic-gl3.vshader", "./shaders/cube-gl3.fshader"} }; static const char * const g_shaderFilesGl2[g_numShaders][3] = { {"./shaders/basic-gl2.vshader", "./shaders/texture-gl2.fshader"}, {"./shaders/basic-gl2.vshader", "./shaders/normal-gl2.fshader"}, {"./shaders/basic-gl2.vshader", "./shaders/cube-gl2.fshader"} }; static vector> g_shaderStates; // our global shader states static shared_ptr g_tex0, g_tex1, g_tex2;
I set up a second and third texture handles in my ShaderState. Note only one texture is actually ever being used at one time, so I could have done this more efficiently in my shader code later
struct ShaderState { GlProgram program; // Handles to uniform variables GLint h_uLight; GLint h_uProjMatrix; GLint h_uModelViewMatrix; GLint h_uNormalMatrix; GLint h_uTexUnit0; GLint h_uTexUnit1; GLint h_uTexUnit2; //won't need coords for cube map // Handles to vertex attributes GLint h_aPosition; GLint h_aNormal; GLint h_aTexCoord0; GLint h_aTexCoord1; ShaderState(const char* vsfn, const char* fsfn) { readAndCompileShader(program, vsfn, fsfn); const GLuint h = program; // short hand // Retrieve handles to uniform variables h_uLight = safe_glGetUniformLocation(h, "uLight"); h_uProjMatrix = safe_glGetUniformLocation(h, "uProjMatrix"); h_uModelViewMatrix = safe_glGetUniformLocation(h, "uModelViewMatrix"); h_uNormalMatrix = safe_glGetUniformLocation(h, "uNormalMatrix"); h_uTexUnit0 = safe_glGetUniformLocation(h, "uTexUnit0"); h_uTexUnit1 = safe_glGetUniformLocation(h, "uTexUnit1"); h_uTexUnit2 = safe_glGetUniformLocation(h, "uTexUnit2"); // Retrieve handles to vertex attributes h_aPosition = safe_glGetAttribLocation(h, "aPosition"); h_aNormal = safe_glGetAttribLocation(h, "aNormal"); h_aTexCoord0 = safe_glGetAttribLocation(h, "aTexCoord0"); h_aTexCoord1 = safe_glGetAttribLocation(h, "aTexCoord1"); if (!g_Gl2Compatible) glBindFragDataLocation(h, 0, "fragColor"); checkGlErrors(); } };
Here we basically, just add a couple of lines for the second texture (we won't need to set up a vbo for the cube map):
void draw(const ShaderState& curSS) { // Enable the attributes used by our shader safe_glEnableVertexAttribArray(curSS.h_aPosition); safe_glEnableVertexAttribArray(curSS.h_aNormal); safe_glEnableVertexAttribArray(curSS.h_aTexCoord0); safe_glEnableVertexAttribArray(curSS.h_aTexCoord1); // bind vbo glBindBuffer(GL_ARRAY_BUFFER, vbo); safe_glVertexAttribPointer(curSS.h_aPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GenericVertex), FIELD_OFFSET(GenericVertex, pos)); safe_glVertexAttribPointer(curSS.h_aNormal, 3, GL_FLOAT, GL_FALSE, sizeof(GenericVertex), FIELD_OFFSET(GenericVertex, normal)); glBindBuffer(GL_ARRAY_BUFFER, texVbo); safe_glVertexAttribPointer(curSS.h_aTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GenericVertex), FIELD_OFFSET(GenericVertex, tex)); safe_glVertexAttribPointer(curSS.h_aTexCoord1, 2, GL_FLOAT, GL_FALSE, sizeof(GenericVertex), FIELD_OFFSET(GenericVertex, tex)); // bind ibo glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); // draw! glDrawElements(GL_TRIANGLES, iboLen, GL_UNSIGNED_SHORT, 0); // Disable the attributes used by our shader safe_glDisableVertexAttribArray(curSS.h_aPosition); safe_glDisableVertexAttribArray(curSS.h_aNormal); safe_glDisableVertexAttribArray(curSS.h_aTexCoord0); safe_glDisableVertexAttribArray(curSS.h_aTexCoord1); }
The interesting part of the new normal map code is how we actually set up the texture. Here we modify to intTextures to set up the second texture, but to get the texture data for this texture we call loadSphereNormalTexture which calculates the values. We will also have a function loadCubeTexture which we'll talk about when we get to cube maps.
Notice glTexParameter* takes the kind of texture, the parameter to set and its value. The magnifcation minification filter and nearest have to do with how we round texture pixel values when we are taking pixels from the texture
static void loadSphereNormalTexture(GLuint type, GLuint texHandle) { int width = 512, height = 512; vector<PackedPixel> pixels; float x = 0; float y = 0; float z = 0; float invRootThree = 1/sqrt(3); pixels.resize(width * height); for (int row = height - 1; row >= 0; row--) { for (int l = 0; l < width; l++) { PackedPixel &p = pixels[row * width + l]; x = invRootThree * ((float)(row - width/2)/(width/2)); y = invRootThree * ((float)(l - height/2)/(height/2)); z = sqrt(1 - x*x - y*y); p.r = (unsigned char)(255 * (x + 1)/2); p.g = (unsigned char)(255 * (y + 1)/2); p.b = (unsigned char)(255 * (z + 1)/2); } } glActiveTexture(type); glBindTexture(GL_TEXTURE_2D, texHandle); glTexImage2D(GL_TEXTURE_2D, 0, g_Gl2Compatible ? GL_RGB : GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]); checkGlErrors(); } static void initTextures() { g_tex0.reset(new GlTexture()); loadTexture(GL_TEXTURE0, *g_tex0, "myphoto.ppm"); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, *g_tex0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); g_tex1.reset(new GlTexture()); loadSphereNormalTexture(GL_TEXTURE1, *g_tex1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, *g_tex1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); g_tex2.reset(new GlTexture()); loadCubeTexture(GL_TEXTURE2, *g_tex2, "one.ppm", "two.ppm", "three.ppm", "four.ppm", "five.ppm", "six.ppm"); glActiveTexture(GL_TEXTURE2); glEnable(GL_TEXTURE_CUBE_MAP); glBindTexture(GL_TEXTURE_CUBE_MAP, *g_tex2); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); }
Notice the fragment shader only uses the texture stuff related to the normal mapping not to the basic or cube map shaders.
We are being lazy here. The normals from the texture are constants for the front face in the original orientation. For other faces in the initial view, we should transform these by how we would need to rotate the initial face to get that face. Further, if we rotate the whole object we should really multiply these normals by the NMVM.
uniform vec3 uLight; uniform sampler2D uTexUnit1; varying vec3 vPosition; varying vec2 vTexCoord1; void main() { vec3 tolight = normalize(uLight - vPosition); vec4 texNormal = texture2D(uTexUnit1, vTexCoord1); vec3 scaledTexNormal = normalize(2.0 * vec3(texNormal) - vec3(1., 1., 1.)); float diffuse = max(0.0, dot(vec3(scaledTexNormal), tolight)); gl_FragColor = vec4(0.7, 0.7, 0.7, 1.0) * diffuse; }
#version 130 uniform vec3 uLight; uniform sampler2D uTexUnit1; in vec3 vPosition; in vec3 vTexCoord1; out vec4 fragColor; void main() { vec3 tolight = normalize(uLight - vPosition); vec4 texNormal = texture2D(uTexUnit1, vTexCoord1); vec3 scaledTexNormal = normalize(2.0 * vec3(texNormal) - vec3(1., 1., 1.)); float diffuse = max(0.0, dot(vec3(scaledTexNormal), tolight)); fragColor = vec4(0.7, 0.7, 0.7, 1) * diffuse; }
Which of the following statements is true?
Recall we called the following function in our initTextures. Here is where we do the work of setting up the faces of our cube map
static void loadCubeTexture(GLuint type, GLuint texHandle, const char *ppmFilename1, const char *ppmFilename2, const char *ppmFilename3, const char *ppmFilename4, const char *ppmFilename5, const char *ppmFilename6) { int texWidth, texHeight; vector<PackedPixel> pixData1, pixData2, pixData3, pixData4, pixData5, pixData6; ppmRead(ppmFilename1, texWidth, texHeight, pixData1); ppmRead(ppmFilename2, texWidth, texHeight, pixData2); ppmRead(ppmFilename3, texWidth, texHeight, pixData3); ppmRead(ppmFilename4, texWidth, texHeight, pixData4); ppmRead(ppmFilename5, texWidth, texHeight, pixData5); ppmRead(ppmFilename6, texWidth, texHeight, pixData6); glActiveTexture(type); glBindTexture(GL_TEXTURE_CUBE_MAP, texHandle); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixData1[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixData2[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixData3[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixData4[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixData5[0]); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, texWidth, texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &pixData6[0]); checkGlErrors(); }
uniform samplerCube uTexUnit2; varying vec3 vNormal; varying vec3 vPosition; vec3 reflect(vec3 w, vec3 n) { return -w + n *(dot(w,n) *2.0); } void main(void) { vec3 normal = normalize(vNormal); vec3 reflected = reflect(normalize(-vPosition), normal); gl_FragColor = textureCube(uTexUnit2, reflected); }
#version 330 uniform samplerCube uTexUnit2; in vec3 vNormal; in vec3 vPosition; out fragColor; vec3 reflect(vec3 w, vec3 n) { return -w + n *(dot(w,n) *2.0); } void main(void) { vec3 normal = normalize(vNormal); vec3 reflected = reflect(normalize(vec3(-vPosition)), normal); vec4 texColor0 = textureCube(texUnit2, reflected); fragColor = texColor0; }
#version 330 uniform mat4 uModelViewmatrix; uniform mat4 uProjMatrix; uniform mat4 uSProjMatrix; uniform mat4 uSModelViewMatrix; in vec4 aVertex; out vec4 aTexCoord; void main() { vTexCoord = uSProjMatrix * uSModelViewMatrix * aVertex; gl_Position = uProjMatrix * uModelViewMatrix * aVertex; }
#version 330 uniform sampler2D vTexUnit0; in vec4 vTexCoord; out fragColor; void main() { vec2 tex2; tex2.x = vTexCoord.x/vTexCoord.w; tex2.y = vTexCoord.y/vTexCoord.w; vec4 texColor0 = texture2D(vTexUnit0, tex2); fragColor = texColor0; }