📄 wmlbumpmap.cpp
字号:
// Magic Software, Inc.
// http://www.magic-software.com
// http://www.wild-magic.com
// Copyright (c) 2003. All Rights Reserved
//
// The Wild Magic Library (WML) source code is supplied under the terms of
// the license agreement http://www.magic-software.com/License/WildMagic.pdf
// and may not be copied or disclosed except in accordance with the terms of
// that agreement.
#include "WmlBumpMap.h"
#include "WmlDirectionalLight.h"
#include "WmlPointLight.h"
#include "WmlRenderer.h"
#include "WmlTriMesh.h"
using namespace Wml;
WmlImplementRTTI(BumpMap,Node);
WmlImplementStream(BumpMap);
//----------------------------------------------------------------------------
BumpMap::BumpMap (Node* pkObjects, Texture* pkNormalMap, Light* pkLight,
bool bModulate)
:
m_spkObjects(pkObjects),
m_spkNormalMap(pkNormalMap),
m_spkLight(pkLight)
{
assert( pkObjects && pkNormalMap && pkLight );
m_bModulate = bModulate;
// Save the cull state of the objects. These objects will be force-culled
// by 'this' to make sure they are not drawn by the regular traversal.
m_bForceCullObjects = pkObjects->ForceCull();
pkObjects->ForceCull() = true;
// create texture states
m_spkTextureState = new TextureState;
m_spkTextureStateModulated = new TextureState;
// set apply mode for normal map
m_spkNormalMap->Apply() = Texture::AM_COMBINE;
m_spkNormalMap->CombineFuncRGB() = Texture::ACF_DOT3_RGB;
m_spkNormalMap->CombineSrc0RGB() = Texture::ACS_TEXTURE;
m_spkNormalMap->CombineOp0RGB() = Texture::ACO_SRC_COLOR;
m_spkNormalMap->CombineSrc1RGB() = Texture::ACS_PRIMARY_COLOR;
m_spkNormalMap->CombineOp1RGB() = Texture::ACO_SRC_COLOR;
// set up the modulated and unmodulated texture states
m_spkTextureState->Set(0,m_spkNormalMap);
m_spkTextureStateModulated->Set(0,m_spkNormalMap);
// The blend color can be modified by the application using the function
// SetCurrentDiffuseMaterial.
Texture* pkTexture = new Texture;
pkTexture->Apply() = Texture::AM_COMBINE;
pkTexture->CombineFuncRGB() = Texture::ACF_MODULATE;
pkTexture->CombineSrc0RGB() = Texture::ACS_CONSTANT;
pkTexture->CombineOp0RGB() = Texture::ACO_SRC_COLOR;
pkTexture->CombineSrc1RGB() = Texture::ACS_PREVIOUS;
pkTexture->CombineOp1RGB() = Texture::ACO_SRC_COLOR;
pkTexture->BlendColor() = pkLight->Diffuse();
m_spkTextureStateModulated->Set(1,pkTexture);
// The blend color can be modified by the application using the function
// SetCurrentAmbientMaterial.
pkTexture = new Texture;
pkTexture->Apply() = Texture::AM_COMBINE;
pkTexture->CombineFuncRGB() = Texture::ACF_ADD;
pkTexture->CombineSrc0RGB() = Texture::ACS_PREVIOUS;
pkTexture->CombineOp0RGB() = Texture::ACO_SRC_COLOR;
pkTexture->CombineSrc1RGB() = Texture::ACS_CONSTANT;
pkTexture->CombineOp1RGB() = Texture::ACO_SRC_COLOR;
pkTexture->BlendColor() = pkLight->Ambient();
m_spkTextureStateModulated->Set(2,pkTexture);
// create an alpha blending state
m_spkAlphaState = new AlphaState;
m_spkAlphaState->BlendEnabled() = true;
m_spkAlphaState->TestEnabled() = false;
m_spkAlphaState->SrcBlend() = AlphaState::SBF_DST_COLOR;
m_spkAlphaState->DstBlend() = AlphaState::DBF_ZERO;
}
//----------------------------------------------------------------------------
BumpMap::BumpMap ()
{
m_bModulate = false;
m_bForceCullObjects = false;
}
//----------------------------------------------------------------------------
BumpMap::~BumpMap()
{
// restore the cull state
m_spkObjects->ForceCull() = m_bForceCullObjects;
// release the render effect objects
m_spkObjects = NULL;
m_spkNormalMap = NULL;
m_spkTextureState = NULL;
m_spkTextureStateModulated = NULL;
m_spkAlphaState = NULL;
m_spkLight = NULL;
}
//----------------------------------------------------------------------------
void BumpMap::ComputeLightVectors (TriMesh& rkMesh)
{
// Generate light direction vectors in the surface local space and store
// them in the trimesh for interpolation via the rasterizer. This assumes
// the user has provided normalized normals.
if ( !rkMesh.Normals() || !rkMesh.Textures() )
return;
assert( rkMesh.Colors() );
// get the world light vector
Vector3f kWLight;
switch ( m_spkLight->GetType() )
{
case Light::LT_DIRECTIONAL:
kWLight = -WmlStaticCast(DirectionalLight,m_spkLight)->Direction();
break;
case Light::LT_POINT:
case Light::LT_SPOT:
kWLight = WmlStaticCast(PointLight,m_spkLight)->Location();
break;
default:
// ambient light, nothing we can do to handle this
return;
}
// transform the world light vector into model space
Vector3f kMLight = kWLight - rkMesh.WorldTranslate();
kMLight = kMLight*rkMesh.WorldRotate();
kMLight /= rkMesh.WorldScale();
// The surface local space information is computed for all the vertices.
// We iterate over the triangles because we need to know the connectivity
// information. The end result is assignment of the light vectors.
Vector3f* akVertex = rkMesh.Vertices();
Vector3f* akNormal = rkMesh.Normals();
Vector2f* akUV = rkMesh.Textures();
// Set the light vectors to (0,0,0) as a flag that the quantity has not
// yet been computed. The probability that a light vector is actually
// (0,0,0) should be small, so the flag system should save computation
// time overall.
ColorRGB* akLVec = rkMesh.Colors();
memset(akLVec,0,rkMesh.GetVertexQuantity()*sizeof(ColorRGB));
for (int iT = 0; iT < rkMesh.GetTriangleQuantity(); iT++)
{
// get the triangle vertices and attributes
int aiIndex[3];
rkMesh.GetTriangle(iT,aiIndex[0],aiIndex[1],aiIndex[2]);
Vector3f* apkV[3] =
{
&akVertex[aiIndex[0]],
&akVertex[aiIndex[1]],
&akVertex[aiIndex[2]]
};
Vector3f* apkN[3] =
{
&akNormal[aiIndex[0]],
&akNormal[aiIndex[1]],
&akNormal[aiIndex[2]]
};
Vector2f* apkUV[3] =
{
&akUV[aiIndex[0]],
&akUV[aiIndex[1]],
&akUV[aiIndex[2]]
};
for (int i = 0; i < 3; i++)
{
ColorRGB& rkColor = akLVec[aiIndex[i]];
if ( rkColor != ColorRGB::BLACK )
continue;
// Compute the surface local space at each vertex.
//
// TO DO. If the geometry is static in model space, then we
// should precompute the surface local space and store it.
//
// The normal vector N is the surface normal. The vertex normal
// is used as an approximation to N.
//
// The tangent vector T is computed by thinking of the surface in
// parametric form P(u,v) for some scalar variables u and v. In
// this case, a tangent is the partial derivative T = dP/du. This
// quantity is estimated from the triangle attributes. The
// texture coordinates (u,v) are used as an approximation to the
// parametric quantities.
//
// The binormal vector is B = Cross(N,T). If the estimation of
// T is ill-posed, we try BLAH BLAH BLAH.
//The tangent T is defined
// by dSurf/dS, and the binormal B = Cross(N,T). We assume the
// normals are normalized, and we assume the model is
// parametrized so that the square patch assumption holds.
//
// We need to compute the tangent vector at the current vertex.
// If we think of the surface as being a vector-valued function
// P(u,v), then we want to set T to be dP/du. We try to compute
// T, but if the parametrization is such that our direct
// definition of T is not applicable, then we try for the binormal
// directly and then back out a reasonable T. If there is no
// useful parametrization information, we just assume that the
// light vector is in the same direction as the surface normal.
int iP = (i == 0) ? 2 : i - 1;
int iN = (i + 1) % 3;
// compute edge vectors at the specified vertex
Vector3f kDP1 = *apkV[iN] - *apkV[i];
Vector3f kDP2 = *apkV[iP] - *apkV[i];
// estimate a tangent surface vector
Vector3f kTangent;
bool fDegenerate = false;
const float fEpsilon = 1e-05f;
if ( Mathf::FAbs(kDP1.Length()) < fEpsilon
|| Mathf::FAbs(kDP2.Length()) < fEpsilon )
{
// The triangle is very small, call it degenerate.
fDegenerate = true;
}
else
{
// difference of surface parameters along triangle edge
float fDU1 = apkUV[iN]->X() - apkUV[i]->X();
float fDV1 = apkUV[iN]->Y() - apkUV[i]->Y();
if ( Mathf::FAbs(fDV1) < fEpsilon )
{
// The triangle effectively has no variation in the v
// texture coordinate.
if ( Mathf::FAbs(fDU1) < fEpsilon )
{
// The triangle effectively has no variation in the u
// coordinate. Since the texture coordinates do not
// effectively vary on this triangle, treat it as a
// degenerate parametric surface.
fDegenerate = true;
}
else
{
// The variation is effectively all in u, so set the
// tangent T = dP/du.
kTangent = kDP1/fDU1;
}
}
else
{
// difference of surface parameters along triangle edge
float fDU2 = apkUV[iP]->X() - apkUV[i]->X();
float fDV2 = apkUV[iP]->Y() - apkUV[i]->Y();
float fDet = fDV1*fDU2 - fDV2*fDU1;
if ( Mathf::FAbs(fDet) >= fEpsilon )
{
// The triangle vertices form three collinear points
// in parameter space, so
// dP/du = (dv1*dP2-dv2*dP1)/(dv1*du2-dv2*du1)
kTangent = (fDV1*kDP2-fDV2*kDP1)/fDet;
}
else
{
// The triangle vertices are collinear in parameter
// space.
fDegenerate = true;
}
}
}
if ( fDegenerate )
{
// The texture coordinate mapping is not properly defined for
// this. Just say that the tangent space light vector points
// in the same direction as the surface normal.
rkColor.r = 0.0f;
rkColor.g = 0.0f;
rkColor.b = 1.0f;
continue;
}
// Project T into the tangent plane by projecting out the surface
// normal, then make it unit length.
kTangent -= apkN[i]->Dot(kTangent)*(*apkN[i]);
kTangent.Normalize();
// compute the binormal B, another tangent perpendicular to T
Vector3f kBinormal = apkN[i]->UnitCross(kTangent);
// When generating bump/normal maps, folks usually work in a
// left-handed screen space with the origin at the upper right,
// u to the right, and v down, while we apply the textures with
// the origin at the lower left, u right, v up, so we need to flip
// the binormal (v-axis) to get a proper transformation to the
// surface local texture space.
Matrix3f kRotTS(
kTangent.X(), kTangent.Y(), kTangent.Z(),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -