www.pudn.com > tomohide_fur.03.13.02.zip > NormalMap.cpp
//----------------------------------------------------------------------------- // File: NormalMap.cpp // // Copyright (C) 2001-2002 Tomohide Kano. All rights reserved. //----------------------------------------------------------------------------- #include#include #include "extensions.h" #include "FurMesh.h" #include "NormalMap.h" //----------------------------------------------------------------------------- // NormalMap //----------------------------------------------------------------------------- NormalMap::NormalMap() { m_Width = 0; m_Height = 0; m_texOffset = 0; m_texNormal = 0; m_texClamp = 0; m_texNormalize = 0; m_vsUpdateOffset = 0; m_fsUpdateOffset = 0; m_vsUpdateNormal = 0; m_fsUpdateNormal = 0; m_PrevVel = D3DXVECTOR4(0, 0, 0, 0); m_Accel = D3DXVECTOR4(0, 0, 0, 0); m_PrevOmega = D3DXVECTOR4(0, 0, 0, 0); m_OmegaAccel = D3DXVECTOR4(0, 0, 0, 0); I_FORCE = 0; I_OMEGA = 0; I_DAMPING = 0; } //----------------------------------------------------------------------------- // ~NormalMap //----------------------------------------------------------------------------- NormalMap::~NormalMap() { glDeleteTextures(1, &m_texOffset); glDeleteTextures(1, &m_texNormal); glDeleteTextures(1, &m_texClamp); glDeleteTextures(1, &m_texNormalize); glDeleteVertexShaderEXT(m_vsUpdateOffset); glDeleteVertexShaderEXT(m_vsUpdateNormal); glDeleteFragmentShaderATI(m_fsUpdateOffset); glDeleteFragmentShaderATI(m_fsUpdateNormal); } //----------------------------------------------------------------------------- // Init //----------------------------------------------------------------------------- bool NormalMap::Init(GLuint width, GLuint height) { m_Width = width; m_Height = height; if (!InitTextures()) return false; if (!InitShaders()) return false; return true; } //----------------------------------------------------------------------------- // InitTextures //----------------------------------------------------------------------------- bool NormalMap::InitTextures() { // offset and normal maps { GLubyte *pixels = new GLubyte[m_Width*m_Height*4]; FillMemory(pixels, m_Width*m_Height*4, 128); // offset map glGenTextures(1, &m_texOffset); glBindTexture(GL_TEXTURE_2D, m_texOffset); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // normal map glGenTextures(1, &m_texNormal); glBindTexture(GL_TEXTURE_2D, m_texNormal); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); delete [] pixels; } // texture map for clamping { int size = 64; GLubyte *pixels = new GLubyte[size*size*4]; GLubyte *p = pixels; for (int j = 0; j < size; j++) for (int i = 0; i < size; i++) { D3DXVECTOR2 vec; vec.x = -1 + 2 * i / float(size - 1); vec.y = -1 + 2 * j / float(size - 1); float len = D3DXVec2Length(&vec); if (len > 1.0) vec /= len; *p++ = GLubyte(128 + 127 * vec.x); *p++ = GLubyte(128 + 127 * vec.y); *p++ = 0; *p++ = 0; } glGenTextures(1, &m_texClamp); glBindTexture(GL_TEXTURE_2D, m_texClamp); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_EXT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_EXT); delete [] pixels; } // texture map for normalization { int size = 64; GLubyte *pixels = new GLubyte[size*size*4]; GLubyte *p = pixels; for (int j = 0; j < size; j++) for (int i = 0; i < size; i++) { D3DXVECTOR3 n; n.x = -1 + 2 * i / float(size - 1); n.y = -1 + 2 * j / float(size - 1); n.z = 0; float len = D3DXVec3Length(&n); if (len > 1.0) n /= len; n.z = 1.0; D3DXVec3Normalize(&n, &n); *p++ = GLubyte(128 + 127 * n.x); *p++ = GLubyte(128 + 127 * n.y); *p++ = GLubyte(128 + 127 * n.z); *p++ = 0; } glGenTextures(1, &m_texNormalize); glBindTexture(GL_TEXTURE_2D, m_texNormalize); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_EXT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_EXT); delete [] pixels; } if (glGetError()) return false; return true; } //----------------------------------------------------------------------------- // InitShaders //----------------------------------------------------------------------------- bool NormalMap::InitShaders() { // variants GLuint V_POS = glBindParameterEXT(GL_CURRENT_VERTEX_EXT); GLuint V_TEX = glBindTextureUnitParameterEXT(GL_TEXTURE0_ARB, GL_CURRENT_TEXTURE_COORDS); GLuint V_DS = glBindTextureUnitParameterEXT(GL_TEXTURE4_ARB, GL_CURRENT_TEXTURE_COORDS); GLuint V_DT = glBindTextureUnitParameterEXT(GL_TEXTURE5_ARB, GL_CURRENT_TEXTURE_COORDS); GLuint V_NORMAL = glBindParameterEXT(GL_CURRENT_NORMAL); // invariants I_FORCE = glGenSymbolsEXT(GL_VECTOR_EXT, GL_INVARIANT_EXT, GL_FULL_RANGE_EXT, 1); I_OMEGA = glGenSymbolsEXT(GL_VECTOR_EXT, GL_INVARIANT_EXT, GL_FULL_RANGE_EXT, 1); I_DAMPING = glGenSymbolsEXT(GL_SCALAR_EXT, GL_INVARIANT_EXT, GL_FULL_RANGE_EXT, 1); ////////////////////////////////////////////////////////////////////// // vertex shader for updating offset map m_vsUpdateOffset = glGenVertexShadersEXT(1); glBindVertexShaderEXT(m_vsUpdateOffset); glBeginVertexShaderEXT(); { // temporary GLuint R_TEMPS = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); GLuint R_TEMPV = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); // vertex position GLuint C_SCALE = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1); GLuint C_BIAS = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1); glSetLocalConstantEXT(C_SCALE, GL_FLOAT, D3DXVECTOR4( 2, 2, 0, 1)); glSetLocalConstantEXT(C_BIAS, GL_FLOAT, D3DXVECTOR4(-1, -1, 0, 0)); glShaderOp3EXT(GL_OP_MADD_EXT, GL_OUTPUT_VERTEX_EXT, V_TEX, C_SCALE, C_BIAS); // texture coord for the previous offset map glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD0_EXT, V_TEX); // force vector GLuint R_FORCE = glGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); glShaderOp2EXT(GL_OP_CROSS_PRODUCT_EXT, R_TEMPV, I_OMEGA, V_POS); glShaderOp2EXT(GL_OP_SUB_EXT, R_FORCE, I_FORCE, R_TEMPV); // transform the force vector into local texture space // (texture space is not necessarily orthonormal) { float one = 1.0; GLuint C_ONE = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_CONSTANT_EXT, GL_FULL_RANGE_EXT, 1); glSetLocalConstantEXT(C_ONE, GL_FLOAT, &one); GLuint R_FS = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); GLuint R_FT = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); GLuint R_ST = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); GLuint R_DET = glGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1); glShaderOp2EXT(GL_OP_DOT3_EXT, R_FS, R_FORCE, V_DS); glShaderOp2EXT(GL_OP_DOT3_EXT, R_FT, R_FORCE, V_DT); glShaderOp2EXT(GL_OP_DOT3_EXT, R_ST, V_DS, V_DT); glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_ST, R_ST); glShaderOp2EXT(GL_OP_SUB_EXT, R_TEMPS, C_ONE, R_TEMPS); glShaderOp1EXT(GL_OP_RECIP_EXT, R_DET, R_TEMPS); glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_FT, R_ST); glShaderOp2EXT(GL_OP_SUB_EXT, R_TEMPS, R_FS, R_TEMPS); glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_TEMPS, R_DET); glInsertComponentEXT(GL_OUTPUT_TEXTURE_COORD1_EXT, R_TEMPS, 0); glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_FS, R_ST); glShaderOp2EXT(GL_OP_SUB_EXT, R_TEMPS, R_FT, R_TEMPS); glShaderOp2EXT(GL_OP_MUL_EXT, R_TEMPS, R_TEMPS, R_DET); glInsertComponentEXT(GL_OUTPUT_TEXTURE_COORD1_EXT, R_TEMPS, 1); } // damping factor glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD2_EXT, I_DAMPING); } glEndVertexShaderEXT(); ////////////////////////////////////////////////////////////////////// // fragment shader for updating offset map m_fsUpdateOffset = glGenFragmentShadersATI(1); glBindFragmentShaderATI(m_fsUpdateOffset); glBeginFragmentShaderATI(); { ////////// first pass ////////// glSampleMapATI(GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI); // offset glPassTexCoordATI(GL_REG_1_ATI, GL_TEXTURE1_ARB, GL_SWIZZLE_STR_ATI); // force glPassTexCoordATI(GL_REG_2_ATI, GL_TEXTURE2_ARB, GL_SWIZZLE_STR_ATI); // damping // REG_2 = 0.5 * force + 0.5 glSetFragmentShaderConstantATI(GL_CON_0_ATI, D3DXVECTOR4(0.5, 0.5, 0, 0)); glColorFragmentOp3ATI(GL_MAD_ATI, GL_REG_1_ATI, GL_NONE, GL_NONE, GL_REG_1_ATI, GL_NONE, GL_NONE, GL_CON_0_ATI, GL_NONE, GL_NONE, GL_CON_0_ATI, GL_NONE, GL_NONE); // REG_0 = damping * REG_2 + (1 - damping) * offset; glColorFragmentOp3ATI(GL_LERP_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_REG_2_ATI, GL_NONE, GL_NONE, GL_REG_1_ATI, GL_NONE, GL_NONE, GL_REG_0_ATI, GL_NONE, GL_NONE); ////////// second pass ////////// glSampleMapATI(GL_REG_1_ATI, GL_REG_0_ATI, GL_SWIZZLE_STR_ATI); // clamp glColorFragmentOp1ATI(GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_REG_1_ATI, GL_NONE, GL_NONE); } glEndFragmentShaderATI(); ////////////////////////////////////////////////////////////////////// // vertex shader for updating normal map m_vsUpdateNormal = glGenVertexShadersEXT(1); glBindVertexShaderEXT(m_vsUpdateNormal); glBeginVertexShaderEXT(); { glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_VERTEX_EXT, V_POS); glShaderOp1EXT(GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD0_EXT, V_TEX); } glEndVertexShaderEXT(); ////////////////////////////////////////////////////////////////////// // fragment shader for updating normal map m_fsUpdateNormal = glGenFragmentShadersATI(1); glBindFragmentShaderATI(m_fsUpdateNormal); glBeginFragmentShaderATI(); { ////////// first pass ////////// glSampleMapATI(GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI); // offset glColorFragmentOp1ATI(GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_REG_0_ATI, GL_NONE, GL_NONE); ////////// second pass ////////// glSampleMapATI(GL_REG_1_ATI, GL_REG_0_ATI, GL_SWIZZLE_STR_ATI); // normalize glColorFragmentOp1ATI(GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_REG_1_ATI, GL_NONE, GL_NONE); } glEndFragmentShaderATI(); if (glGetError()) return false; return true; } //----------------------------------------------------------------------------- // Update //----------------------------------------------------------------------------- void NormalMap::Update(FurMesh *pMesh, float dt, const D3DXVECTOR4& gravity, const D3DXVECTOR4& velocity, const D3DXVECTOR4& omega) { // compute invariants { // filter coefficient for calculating m_Acceleration float filter = min(0.25, dt/0.1); // translational m_Acceleration m_Accel = (1.0 - filter) * m_Accel + filter * ((velocity - m_PrevVel) / dt); m_PrevVel = velocity; // translational force = gravity + air resistance + inertia // (coefficients were determined experimentally) glSetInvariantEXT(I_FORCE, GL_FLOAT, (void*)&(gravity - 0.12*velocity - 0.018*m_Accel)); // angular m_Acceleration m_OmegaAccel = (1.0 - filter) * m_OmegaAccel + filter * ((omega - m_PrevOmega) / dt); m_PrevOmega = omega; // angular force = -CrossProduct(I_OMEGA, vertex_pos) glSetInvariantEXT(I_OMEGA, GL_FLOAT, (void*)&(0.12*omega + 0.018*m_OmegaAccel)); // damping factor float damping = max(0.0625, min(0.5, dt/0.3)); glSetInvariantEXT(I_DAMPING, GL_FLOAT, &damping); } glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); glViewport(0, 0, m_Width, m_Height); glDisable(GL_DEPTH_TEST); glEnable(GL_VERTEX_SHADER_EXT); glEnable(GL_FRAGMENT_SHADER_ATI); // update offset map { glPushAttrib(GL_TEXTURE_BIT); glBindVertexShaderEXT(m_vsUpdateOffset); glBindFragmentShaderATI(m_fsUpdateOffset); glActiveTextureARB(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_texOffset); glActiveTextureARB(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_texClamp); glActiveTextureARB(GL_TEXTURE0_ARB); pMesh->Draw(); glFinish(); glBindTexture(GL_TEXTURE_2D, m_texOffset); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_Width, m_Height); glPopAttrib(); } // update normal map { glPushAttrib(GL_TEXTURE_BIT); glBindVertexShaderEXT(m_vsUpdateNormal); glBindFragmentShaderATI(m_fsUpdateNormal); glActiveTextureARB(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_texOffset); glActiveTextureARB(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, m_texNormalize); glActiveTextureARB(GL_TEXTURE0_ARB); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(-1, -1); glTexCoord2f(1, 0); glVertex2f( 1, -1); glTexCoord2f(1, 1); glVertex2f( 1, 1); glTexCoord2f(0, 1); glVertex2f(-1, 1); glEnd(); glFinish(); glBindTexture(GL_TEXTURE_2D, m_texNormal); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_Width, m_Height); glPopAttrib(); } glPopAttrib(); } //----------------------------------------------------------------------------- // ResetInertia //----------------------------------------------------------------------------- void NormalMap::ResetInertia() { m_PrevVel = D3DXVECTOR4(0, 0, 0, 0); m_Accel = D3DXVECTOR4(0, 0, 0, 0); m_PrevOmega = D3DXVECTOR4(0, 0, 0, 0); m_OmegaAccel = D3DXVECTOR4(0, 0, 0, 0); } //----------------------------------------------------------------------------- // Bind //----------------------------------------------------------------------------- void NormalMap::Bind() const { glBindTexture(GL_TEXTURE_2D, m_texNormal); } //----------------------------------------------------------------------------- // BindOffsetMap //----------------------------------------------------------------------------- // binds offset map (only used for viewing) void NormalMap::BindOffsetMap() const { glBindTexture(GL_TEXTURE_2D, m_texOffset); }