OpenGL 几何图元





5.00/5 (10投票s)
使用纹理和光照效果在 OpenGL 中构建盒子、圆柱体、圆锥体和球体
引言
我想编写一个简单的 X3DOM 查看器,基于 OpenGL,并在网络上搜索了有关构建盒子、圆柱体、圆锥体和球体的资料。我发现了很多好的论坛帖子,但没有一个是“现成的”。在本技巧中,我想与所有也在寻找此主题入门的人分享我的发现。因此,请不要期望在下面看到“火箭科学”,而只是“现成的”代码。
我还在编写简单的 X3DOM 查看器时,写了另一篇关于“光源特征和相关材质属性”的技巧。
背景
X3DOM 规范支持 Box
、Cylinder
、Cone
和 Sphere
对象,所以我需要一种构建这些图形图元的算法。
我对 OpenGL 还不熟悉,所以对我来说,这些算法易于理解和遵循非常重要。(我的一个参考资料是 OpenGL 编程指南。)
结果是支持所有类型 OpenGL 光照(环境光、漫反射光和镜面反射光)以及纹理的几何图元。不幸的是,在 OpenGL 中同时使用所有类型的光照和纹理需要一些技巧和扩展 (EXT_secondary_color
),但这将是下一个技巧的内容。以下图片展示了使用现有手段已经可以实现的效果
具有全光照的几何图元
具有简单纹理的几何图元
带有光谱颜色的三角形标记了每种情况下光源的位置。
对于圆锥体和球体,您可以清楚地看到通过平面表面分段对弯曲表面的近似。近似的质量可以通过用于平面表面分段的角度的步长来调整 - 对于图片,我使用每 360° 圆周 60 步。
Using the Code
Box
让我们从盒子开始,直接的代码 - 没有花哨的技巧
/// <summary>
/// Draws a box into the scene.
/// The default orientation (on identity GL_MODELVIEW matrix) is upright.
/// The default center position (on identity GL_MODELVIEW matrix) is [0.0, 0.0, 0.0].
/// </summary>
/// <param name="fWidthHalf">Specifies the half edge length in x direction.</param>
/// <param name="fWidthHalf">Specifies the half edge length in y direction.</param>
/// <param name="fDepthHalf">Specifies the half edge length in z direction.</param>
/// <param name="bTexture">Determine whether texture coordinates shall be calculated.</param>
/// <remarks>
/// +-----------+ y (height)
/// / top /| /|\
/// / / | |
/// +-----------+ | | \
/// | | right +-------> x (width)
/// | front | + / /
/// | | / /
/// | |/ |/_
/// +-----------+ z (depth)
/// </remarks>
void OpenGL::DrawBox(float fWidthHalf, float fHeightHalf, float fDepthHalf, bool bTexture)
{
// -- FRONT
::glBegin(GL_TRIANGLES);
::glNormal3f(0.0f, 0.0f, +1.0f);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 0.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 1.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, +fDepthHalf);
::glEnd();
// -- BACK
::glBegin(GL_TRIANGLES);
::glNormal3f(0.0f, 0.0f, -1.0f);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 0.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 1.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, -fDepthHalf);
::glEnd();
// -- LEFT
::glBegin(GL_TRIANGLES);
::glNormal3f(-1.0f, 0.0f, 0.0f);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 0.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 1.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, -fDepthHalf);
::glEnd();
// -- RIGHT
::glBegin(GL_TRIANGLES);
::glNormal3f(+1.0f, 0.0f, 0.0f);
if (bTexture) ::glTexCoord2f(0.0f, 0.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 1.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, +fDepthHalf);
::glEnd();
// -- TOP
::glBegin(GL_TRIANGLES);
::glNormal3f(0.0f, +1.0f, 0.0f);
if (bTexture) ::glTexCoord2f(0.0f, 0.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, +fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 1.0f);
::glVertex3f(+fWidthHalf, +fHeightHalf, +fDepthHalf);
::glEnd();
// BOTTOM
::glBegin(GL_TRIANGLES);
::glNormal3f(0.0f, -1.0f, 0.0f);
if (bTexture) ::glTexCoord2f(0.0f, 0.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(0.0f, 1.0f);
::glVertex3f(-fWidthHalf, -fHeightHalf, +fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 0.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, -fDepthHalf);
if (bTexture) ::glTexCoord2f(1.0f, 1.0f);
::glVertex3f(+fWidthHalf, -fHeightHalf, +fDepthHalf);
::glEnd();
}
圆柱体
/// <summary> /// Draws a cylinder into the scene. /// The default orientation (on identity GL_MODELVIEW matrix) is upright. /// The default center position (on identity GL_MODELVIEW matrix) is [0.0, 0.0, 0.0]. /// </summary> /// <param name="fWidthHalf">Specifies the radius in x direction.</param> /// <param name="fWidthHalf">Specifies the half height in y direction.</param> /// <param name="fDepthHalf">Specifies the radius in z direction.</param> /// <param name="dAngleStep">Specifies the angle in radiants, plain surface sections /// approximate a curved surface.</param> /// <param name="pColors">Exactly three colors for top, cover and bottom or <c>NULL</c> /// if no individual colors are to apply.</param> /// <param name="bTexture">Determine whether texture coordinates shall be calculated.</param> /// <remarks> /// _------_ y (height) /// / \ top /|\ /// |\_ _/| | /// | ------ | | \ /// | | cover +-------> x (width) /// | | / / /// | | / /// \_ _/ bottom |/_ /// ------ z (depth) /// </remarks> void OpenGL::DrawCylinder(float fWidthHalf, float fHeightHalf, float fDepthHalf, double dAngleStep, const COLORREF* pColors = NULL, bool bTexture) { float fReciprocalPrecisition = (float)(dAngleStep / M_2PI); // Cylinder top if (pColors != NULL) ::glColor3f(MIN(1.0F, GetRValue(pColors[0]) / 255.0F), MIN(1.0F, GetGValue(pColors[0]) / 255.0F), MIN(1.0F, GetBValue(pColors[0]) / 255.0F)); int iSectorCount = 0; ::glBegin(GL_POLYGON); // ? TRIANGLE_FAN ? for (double dTopAngle = M_2PI; dTopAngle > 0.0; dTopAngle -= dAngleStep) { // face normal ::glNormal3f(0.0F, +1.0F, 0.0F); double c = cos(dTopAngle); double s = sin(dTopAngle); // relative texture coordinates float fTextureOffsetS = 0.5F + (float)(0.5F * c); float fTextureOffsetT = 0.5F + (float)(0.5F * s); if (bTexture) ::glTexCoord2f(fTextureOffsetS, fTextureOffsetT); ::glVertex3f((float)(fWidthHalf * cos(dTopAngle)), fHeightHalf, (float)(fDepthHalf * sin(dTopAngle))); } ::glEnd(); // Cylinder cover if (pColors != NULL) ::glColor3f(MIN(1.0F, GetRValue(pColors[1]) / 255.0F), MIN(1.0F, GetGValue(pColors[1]) / 255.0F), MIN(1.0F, GetBValue(pColors[1]) / 255.0F)); iSectorCount = 0; ::glBegin(GL_TRIANGLES); for (double dCoverAngle1 = 0; dCoverAngle1 < M_2PI; dCoverAngle1 += dAngleStep) { double dCoverAngle2 = dCoverAngle1 + dAngleStep; float fWidthPart1 = (float)(fWidthHalf * cos(dCoverAngle1)); float fWidthPart2 = (float)(fWidthHalf * cos(dCoverAngle2)); float fDepthPart1 = (float)(fDepthHalf * sin(dCoverAngle1)); float fDepthPart2 = (float)(fDepthHalf * sin(dCoverAngle2)); // face normal ::glNormal3f((float)(1.0F * cos(dCoverAngle1 + dAngleStep / 2)), 0.0F, (float)(1.0F * sin(dCoverAngle1 + dAngleStep / 2))); // relative texture coordinates float fTextureOffsetS1 = (float)(iSectorCount ) * fReciprocalPrecisition; float fTextureOffsetT1 = 0.0F; float fTextureOffsetS2 = (float)(iSectorCount + 1) * fReciprocalPrecisition; float fTextureOffsetT2 = 1.0F; if (bTexture) ::glTexCoord2f(fTextureOffsetS1, fTextureOffsetT1); ::glVertex3f(fWidthPart1, -fHeightHalf, fDepthPart1); if (bTexture) ::glTexCoord2f(fTextureOffsetS1, fTextureOffsetT2); ::glVertex3f(fWidthPart1, fHeightHalf, fDepthPart1); if (bTexture) ::glTexCoord2f(fTextureOffsetS2, fTextureOffsetT2); ::glVertex3f(fWidthPart2, fHeightHalf, fDepthPart2); if (bTexture) ::glTexCoord2f(fTextureOffsetS2, fTextureOffsetT2); ::glVertex3f(fWidthPart2, fHeightHalf, fDepthPart2); if (bTexture) ::glTexCoord2f(fTextureOffsetS2, fTextureOffsetT1); ::glVertex3f(fWidthPart2, -fHeightHalf, fDepthPart2); if (bTexture) ::glTexCoord2f(fTextureOffsetS1, fTextureOffsetT1); ::glVertex3f(fWidthPart1, -fHeightHalf, fDepthPart1); iSectorCount++; } ::glEnd(); // Cylinder bottom if (pColors != NULL) ::glColor3f(MIN(1.0F, GetRValue(pColors[2]) / 255.0F), MIN(1.0F, GetGValue(pColors[2]) / 255.0F), MIN(1.0F, GetBValue(pColors[2]) / 255.0F)); iSectorCount = 0; ::glBegin(GL_POLYGON); // ? TRIANGLE_FAN ? for (double dBottotmAngle = 0; dBottotmAngle < M_2PI; dBottotmAngle += dAngleStep) { // face normal ::glNormal3f(0.0F, -1.0F, 0.0F); double c = cos(dBottotmAngle); double s = sin(dBottotmAngle); // relative texture coordinates float fTextureOffsetS = 0.5F + (float)(0.5F * c); float fTextureOffsetT = 0.5F + (float)(0.5F * s); if (bTexture) ::glTexCoord2f(fTextureOffsetS, fTextureOffsetT); ::glVertex3f((float)(fWidthHalf * cos(dBottotmAngle)), -fHeightHalf, (float)(fDepthHalf * sin(dBottotmAngle))); } ::glEnd(); }
圆锥体
/// <summary>
/// Draws a cone into the scene.
/// The default orientation (on identity GL_MODELVIEW matrix) is upright.
/// The default center position (on identity GL_MODELVIEW matrix) is [0.0, 0.0, 0.0].
/// </summary>
/// <param name="fWidthHalf">Specifies the radius in x direction.</param>
/// <param name="fWidthHalf">Specifies the half height in y direction.</param>
/// <param name="fDepthHalf">Specifies the radius in z direction.</param>
/// <param name="dAngleStep">Specifies the angle in radiants, plain surface sections
approximate a curved surface.</param>
/// <param name="pColors">Exactly two colors for cover and bottom or <c>NULL</c>
/// if no individual colors are to apply.</param>
/// <param name="bTexture">Determine whether texture coordinates shall be calculated.
/// </param>
/// <remarks>
/// /\ y (height)
/// | | top /|\
/// / \ |
/// | | | \
/// /_------_\ cover +-------> x (width)
/// |/ \| / /
/// | | /
/// \_ _/ bottom |/_
/// ------ z (depth)
/// </remarks>
void DrawCone(float fWidthHalf, float fHeightHalf, float fDepthHalf,
double dAngleStep, const COLORREF* pColors = NULL, bool bTexture = false);
{
float fReciprocalPrecisition = (float)(dAngleStep / M_2PI);
// Cone "Cover"
if (pColors != NULL)
::glColor3f(MIN(1.0F, GetRValue(pColors[0]) / 255.0F),
MIN(1.0F, GetGValue(pColors[0]) / 255.0F),
MIN(1.0F, GetBValue(pColors[0]) / 255.0F));
int iSectorCount = 0;
::glBegin(GL_TRIANGLES);
for (double dCoverAngle1 = 0.0F; dCoverAngle1 < M_2PI; dCoverAngle1 += dAngleStep)
{
double dCoverAngle2 = dCoverAngle1 + dAngleStep;
float fWidthPart1 = (float)(fWidthHalf * cos(dCoverAngle1));
float fWidthPart2 = (float)(fWidthHalf * cos(dCoverAngle2));
float fDepthPart1 = (float)(fDepthHalf * sin(dCoverAngle1));
float fDepthPart2 = (float)(fDepthHalf * sin(dCoverAngle2));
// face normal
::glNormal3f((fWidthPart1 + fWidthPart2) / 2, 0.0F, (fDepthPart1 + fDepthPart2) / 2);
// relative texture coordinates
float fTextureOffsetS1 = iSectorCount * fReciprocalPrecisition;
float fTextureOffsetS2 = (iSectorCount + 1) * fReciprocalPrecisition;
if (bTexture) ::glTexCoord2f(fTextureOffsetS1, 1.0);
::glVertex3f(0.0F, fHeightHalf, 0.0F);
if (bTexture) ::glTexCoord2f(fTextureOffsetS2, 0.0);
::glVertex3f(fWidthPart2, -fHeightHalf, fDepthPart2);
if (bTexture) ::glTexCoord2f(fTextureOffsetS2, 0.0);
::glVertex3f(fWidthPart1, -fHeightHalf, fDepthPart1);
iSectorCount++;
}
::glEnd();
// Cone Bottom
if (pColors != NULL)
::glColor3f(MIN(1.0F, GetRValue(pColors[1]) / 255.0F),
MIN(1.0F, GetGValue(pColors[1]) / 255.0F),
MIN(1.0F, GetBValue(pColors[1]) / 255.0F));
iSectorCount = 0;
::glBegin(GL_POLYGON); // ? TRIANGLE_FAN ?
for (double dBottomAngle = 0; dBottomAngle < M_2PI; dBottomAngle += dAngleStep)
{
// face normal
::glNormal3f(0.0F, -1.0F, 0.0F);
double c = cos(dBottomAngle);
double s = sin(dBottomAngle);
// relative texture coordinates
float fTextureOffsetS = 0.5F + (float)(0.5F * c);
float fTextureOffsetT = 0.5F + (float)(0.5F * s);
if (bTexture) ::glTexCoord2f(fTextureOffsetS, fTextureOffsetT);
::glVertex3f((float)(fWidthHalf * c), -fHeightHalf, (float)(fDepthHalf * s));
}
::glEnd();
}
球体
/// <summary>
/// Draws a sphere into the scene.
/// The default orientation (on identity GL_MODELVIEW matrix) is upright.
/// The default center position (on identity GL_MODELVIEW matrix) is [0.0, 0.0, 0.0].
/// </summary>
/// <param name="fWidthHalf">Specifies the radius in x direction.</param>
/// <param name="fWidthHalf">Specifies the radius in y direction.</param>
/// <param name="fDepthHalf">Specifies the radius in z direction.</param>
/// <param name="dAngleStep">Specifies the angle in radiants, plain surface sections
approximate a curved surface.</param>
/// <param name="bTexture">Determine whether texture coordinates shall be calculated.</param>
/// <remarks>
/// stack -----sector-----> y (height)
/// _-------_ | _-------_ /|\
/// / \ | / / | \ \ |
/// /___ ___\ | / / | \ \ | \
/// | ----- | | | | | | | +-------> x (width)
/// |___ ___| | | | | | | / /
/// \ ------- / | \ \ | / / /
/// \_ _/ | \_ \ | / _/ |/_
/// ------- \|/ ------- z (depth)
/// </remarks>
void DrawSphere(float fWidthHalf, float fHeightHalf, float fDepthHalf,
double dAngleStep, bool bTexture = false)
{
float f1_10thPi = (float)(M_PI * 0.1f);
float f2_10thPi = (float)(M_PI * 0.2f);
float f3_10thPi = (float)(M_PI * 0.3f);
float f7_10thPi = (float)(M_PI * 0.7f);
float f8_10thPi = (float)(M_PI * 0.8f);
float f9_10thPi = (float)(M_PI * 0.9f);
float fPiQuarter = (float)(M_PI * 0.25f);
float fPiHalf = (float)(M_PI * 0.50f);
float fReciprocalPrecisition = dAngleStep / M_2PI;
int iStackCount = 0;
::glBegin(GL_TRIANGLES);
// Plane: X/Y; Rotation axis: Z; 0° in positive X direction
for (double dStackAngle1 = 0.0f; dStackAngle1 < M_PI; dStackAngle1 += dAngleStep)
{
double dStackAngle2 = dStackAngle1 + dAngleStep;
double s1 = sin(dStackAngle1);
double s2 = sin(dStackAngle2);
float fStackHeightHalf1 = (float)(-fHeightHalf * cos(dStackAngle1));
float fStackHeightHalf2 = (float)(-fHeightHalf * cos(dStackAngle2));
float fStackWidthHalfTop = (float)(fWidthHalf * s1);
float fStackWidthHalfBtm = (float)(fWidthHalf * s2);
float fStackDepthHalfTop = (float)(fDepthHalf * s1);
float fStackDepthHalfBtm = (float)(fDepthHalf * s2);
int iSectorCount = 0;
// Plane: X/Z; Rotation axis: Y; 0° in positive X direction
for (double dSectorAngle1 = 0.0f; dSectorAngle1 < M_2PI; dSectorAngle1 += dAngleStep)
{
double dSectorAngle2 = dSectorAngle1 + dAngleStep;
// face corners
float fLeftTop[3] = { (float)(fStackWidthHalfTop * cos(dSectorAngle1)),
fStackHeightHalf1,
(float)(fStackDepthHalfTop * sin(dSectorAngle1)) };
float fRghtTop[3] = { (float)(fStackWidthHalfTop * cos(dSectorAngle2)),
fStackHeightHalf1,
(float)(fStackDepthHalfTop * sin(dSectorAngle2)) };
float fLeftBtm[3] = { (float)(fStackWidthHalfBtm * cos(dSectorAngle1)),
fStackHeightHalf2,
(float)(fStackDepthHalfBtm * sin(dSectorAngle1)) };
float fRghtBtm[3] = { (float)(fStackWidthHalfBtm * cos(dSectorAngle2)),
fStackHeightHalf2,
(float)(fStackDepthHalfBtm * sin(dSectorAngle2)) };
// face normal
float n[3] = { (fLeftTop[0] + fRghtBtm[0]) / 2,
(fStackHeightHalf1 + fStackHeightHalf2) / 2,
(fLeftTop[2] + fRghtBtm[2]) / 2 };
// relative texture coordinates
float fTextureOffsetS1 = iSectorCount * fReciprocalPrecisition;
float fTextureOffsetT1 = 2 * iStackCount * fReciprocalPrecisition;
float fTextureOffsetS2 = (iSectorCount + 1) * fReciprocalPrecisition;
float fTextureOffsetT2 = 2 * (iStackCount + 1) * fReciprocalPrecisition;
if (fLeftBtm[0] != fRghtBtm[0] || fLeftBtm[2] != fRghtBtm[2])
{
// face normal
::glNormal3f(n[0], n[1], n[2]);
if (bTexture) ::glTexCoord2f(fTextureOffsetS1, fTextureOffsetT1);
::glVertex3fv(fLeftTop);
if (bTexture) ::glTexCoord2f(fTextureOffsetS1, fTextureOffsetT2);
::glVertex3fv(fLeftBtm);
if (bTexture) ::glTexCoord2f(fTextureOffsetS2, fTextureOffsetT2);
::glVertex3fv(fRghtBtm);
}
if (fLeftTop[0] != fRghtTop[0] || fLeftTop[2] != fRghtTop[2])
{
// face normal
::glNormal3f(n[0], n[1], n[2]);
if (bTexture) ::glTexCoord2f(fTextureOffsetS1, fTextureOffsetT1);
::glVertex3fv(fLeftTop);
if (bTexture) ::glTexCoord2f(fTextureOffsetS2, fTextureOffsetT2);
::glVertex3fv(fRghtBtm);
if (bTexture) ::glTexCoord2f(fTextureOffsetS2, fTextureOffsetT1);
::glVertex3fv(fRghtTop);
}
iSectorCount++;
}
iStackCount++;
}
::glEnd();
}
环境
Box
、Cylinder
、Cone
和 Sphere
的调用环境就像一个模子里刻出来的 - 这就是为什么我只在这里展示 Box
的调用环境
void Box::RenderToOpenGL(X3DNode* pParentNode, HDC dcOpenGlWindow)
{
Vector3f size = GetSize();
GLuint uiTextureID = 0;
X3DShapeNode* pParentShape = dynamic_cast<X3DShapeNode*>(pParentNode);
if (pParentShape != NULL)
{
X3DAppearanceNode* pAppearence = pParentShape->GetAppearance();
if ((Appearance*)pAppearence != NULL)
{
((Appearance*)pAppearence)->ApplyMaterial(dcOpenGlWindow);
uiTextureID = ((Appearance*)pAppearence)->ApplyTexture(dcOpenGlWindow);
}
}
if (uiTextureID != 0)
{
::glBindTexture(GL_TEXTURE_2D, uiTextureID);
::glEnable(GL_TEXTURE_2D);
}
OpenGL::DrawBox(size.x * 0.5f, size.y * 0.5f, size.z * 0.5f, (uiTextureID != 0));
if (uiTextureID != 0)
{
::glDisable(GL_TEXTURE_2D);
::glBindTexture(GL_TEXTURE_2D, 0);
}
}
由于我的应用程序将是一个 X3DOM 查看器,我也会使用 X3DOM 的类层次结构。根据这个,Material
和 Texture
(按 Appearance
分组) 以及 Box
/Cylinder
/Cone
/Sphere
都是 Shape
的子级。
材质和纹理
以下代码位于 ApplyMaterial(...)
和 ApplyTexture(...)
之后 (目前只实现了 X3DOM 规范的一小部分)
/// <summary>Applies the <c>X3DMaterialNode</c> to the subsequent scene objects.</summary>
/// <param name="dcOpenGlWindow">The device context of the OpenGL window.</param>
void Appearance::ApplyMaterial(HDC dcOpenGlWindow)
{
X3DMaterialNode* pMaterial = GetMaterial();
if (pMaterial != NULL)
{
SFColor oColor = pMaterial->GetDiffuseColor();
::glColor3f(oColor.r, oColor.g, oColor.b);
}
else
{
SFColor oColor = CSSColors::Colors[CSSColors::IndexOf(L"Gray")].Value;
::glColor3f(oColor.r, oColor.g, oColor.b);
}
}
/// <summary>Applies the <c>X3DTextureNode</c> to the subsequent scene objects.</summary>
/// <param name="dcOpenGlWindow">The device context of the OpenGL window.</param>
/// <returns>The applied texture (name/id) on success, or <c>0</c> otherwise.</returns>
GLuint Appearance::ApplyTexture(HDC dcOpenGlWindow)
{
GLuint uiTextureID = 0;
X3DTextureNode* pTexture = GetTexture();
if (pTexture != NULL)
{
String strUri = pTexture->GetUri();
if (!String::IsNullOrEmpty(strUri))
{
uiTextureID = OpenGL::GetTexture(strUri.Value());
if (uiTextureID == 0)
{
uiTextureID = OpenGL::AddTexture(strUri.Value(), dcOpenGlWindow);
}
}
}
return uiTextureID;
}
为了完整起见,这里是 AddTexture(...)
的代码 (目前仅支持 24bpp 位图,例如,它们可以使用 MS Paint 创建)
/// <summary>
/// Register a new texture (name/id) based on the indicated image URI.
/// </summary>
/// <param name="wszFilename">The file name of the bitmap file to load.</param>
/// <param name="dcOpenGlWindow">The device context of the OpenGL window.</param>
/// <returns>The texture (name/id) on success, or <c>0</c> otherwise.</returns>
GLuint OpenGL::AddTexture(const WCHAR* wszFilename, HDC dcOpenGlWindow)
{
GLuint uiTextureID = 0;
::glGenTextures(1, &uiTextureID);
if (uiTextureID == 0)
{
Console::WriteError(
L"OpenGL::AddTexture() Failed to acquire a new texture (name/id)!\n");
return uiTextureID;
}
int iWidth = 0;
int iHeight = 0;
BYTE* pbyBitmapPixel = NULL;
if (OpenGL::LoadTextureImageFile(wszFilename, dcOpenGlWindow, iWidth, iHeight,
&pbyBitmapPixel))
{
::glEnable(GL_TEXTURE_2D);
::glBindTexture(GL_TEXTURE_2D, uiTextureID);
// set the texture wrapping
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// scale linearly when image bigger/smaller than texture
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
::glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
::glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, iWidth, iHeight, 0,
GL_BGR_EXT, GL_UNSIGNED_BYTE, pbyBitmapPixel);
__GLEXTFP_GLGENERATEMIPMAPS __glextGenerateMipmaps =
(__GLEXTFP_GLGENERATEMIPMAPS)__GLEXT_GetProcAddress("glGenerateMipmaps");
if (__glextGenerateMipmaps != NULL)
__glextGenerateMipmaps(GL_TEXTURE_2D);
else
Console::WriteWarning(
L" OpenGL::AddTexture() Extension 'glGenerateMipmaps' not available!\n");
//::gluBuild2DMipmaps(GL_TEXTURE_2D, 3, iWidth, iHeight, GL_BGR_EXT,
// GL_UNSIGNED_BYTE, pbyBitmapPixel);
free(pbyBitmapPixel);
pbyBitmapPixel = NULL;
::glBindTexture(GL_TEXTURE_2D, 0);
::glDisable(GL_TEXTURE_2D);
_textures[wszFilename] = uiTextureID;
}
else
{
Console::WriteError(
L"OpenGL::AddTexture() Failed to assign bitmap to new texture (name/id)!\n");
::glDeleteTextures(1, &uiTextureID);
uiTextureID = 0;
}
return uiTextureID;
}
就这样。祝您在 OpenGL 中玩得开心!
关注点
即使 ::glBegin(GL_TRIANGLES)
和 ::glEnd()
背后的数学是清楚的 - 正确设置所有坐标也需要一些反复试验。 ::glNormal3f(...)
和 ::glTexCoord2f(...)
也是如此。
历史
- 2021年3月10日:初始技巧
- 2021年4月17日:添加了 pColors 参数到圆柱体和圆锥体