如何让3D场景中伤害数字始终朝向旋转的相机?
问题描述
实现了绕角色旋转的3D相机,同时用RenderNumber函数在角色上方绘制伤害数字,但伤害数字无法随相机旋转始终面向镜头,视角变化时可读性差。相关渲染代码如下:
RenderNumber 函数
void RenderNumber(vec3_t Position,int Num,vec3_t Color,float Alpha,float Scale) { vec3_t p; VectorCopy(Position,p); vec3_t Light[4]; VectorCopy(Color,Light[0]); VectorCopy(Color,Light[1]); VectorCopy(Color,Light[2]); VectorCopy(Color,Light[3]); if(Num == -1) { float UV[4][2]; TEXCOORD(UV[0],0.f ,32.f/32.f); TEXCOORD(UV[1],32.f/256.f,32.f/32.f); TEXCOORD(UV[2],32.f/256.f,17.f/32.f); TEXCOORD(UV[3],0.f ,17.f/32.f); RenderSpriteUV(BITMAP_FONT+1,p,45,20,UV,Light,Alpha); } else if(Num == -2) { RenderSprite(BITMAP_FONT_HIT,p,32*Scale,20*Scale, Light[0], 0.f, 0.f, 0.f, 27.f/32.f, 15.f/16.f); } else { char Text[32]; itoa(Num,Text,10); p[0] -= strlen(Text)*5.f; unsigned int Length = strlen(Text); p[0] -= Length*Scale*0.125f; p[1] -= Length*Scale*0.125f; for(unsigned int i=0;i<Length;i++) { float UV[4][2]; float u = (float)(Text[i]-48)*16.f/256.f; TEXCOORD(UV[0],u ,16.f/32.f); TEXCOORD(UV[1],u+16.f/256.f,16.f/32.f); TEXCOORD(UV[2],u+16.f/256.f,0.f); TEXCOORD(UV[3],u ,0.f); RenderSpriteUV(BITMAP_FONT+1,p,Scale,Scale,UV,Light,Alpha); p[0] += Scale*0.5f; p[1] += Scale*0.5f; } } }
RenderSpriteUV 函数
void RenderSpriteUV(int Texture,vec3_t Position,float Width,float Height,float (*UV)[2],vec3_t Light[4],float Alpha) { BindTexture(Texture); vec3_t p2; VectorTransform(Position,CameraMatrix,p2); float x = p2[0]; float y = p2[1]; float z = p2[2]; Width *= 0.5f; Height *= 0.5f; vec3_t p[4]; Vector(x-Width, y-Height, z, p[0]); Vector(x+Width, y-Height, z, p[1]); Vector(x+Width, y+Height, z, p[2]); Vector(x-Width, y+Height, z, p[3]); glBegin(GL_QUADS); for(int i=0;i<4;i++) { glColor4f(Light[i][0],Light[i][1],Light[i][2],Alpha); glTexCoord2f(UV[i][0],UV[i][1]); glTexCoord3f(UV[i][0],UV[i][1],100.f); glVertex3fv(p[i]); } glEnd(); }
粒子创建与渲染函数
void CreatePoint(vec3_t Position,int Value,vec3_t Color,float scale, bool bMove, bool bRepeatedly) { for(int i=0;i<MAX_POINTS;i++) { PARTICLE *o = &Points[i]; if(!o->Live) { o->Live = true; o->Type = Value; VectorCopy(Position,o->Position); o->Position[2] += 140.f; VectorCopy(Color,o->Color); o->bRepeatedly = bRepeatedly; o->fRepeatedlyHeight = RequestTerrainHeight(o->Position[0], o->Position[1])+140.0f; o->Gravity = 10.f; o->Scale = scale; o->LifeTime = 0; o->bEnableMove = bMove; return; } } } void RenderPoints( BYTE byRenderOneMore ) { EnableAlphaTest(); DisableDepthTest(); for(int i=0;i<MAX_POINTS;i++) { PARTICLE *o = &Points[i]; if(o->Live) { if ( byRenderOneMore==1 ) { if ( o->Position[2]>350.f ) continue; } else if ( byRenderOneMore==2 ) { if ( o->Position[2]<=300.f ) continue; } else if(o->bRepeatedly) { if( o->Position[2] <= o->fRepeatedlyHeight) continue; } RenderNumber(o->Position,o->Type,o->Color,o->Gravity*0.4f,o->Scale); } } }
解决方案
核心思路是让伤害数字的四边形始终面向相机,通过相机的右向、上向向量构建世界空间的四边形,再转换到相机空间渲染。
1. 修改 RenderSpriteUV 函数
替换原实现,改为基于相机向量构建面向镜头的四边形:
void RenderSpriteUV(int Texture, vec3_t Position, float Width, float Height, float (*UV)[2], vec3_t Light[4], float Alpha) { BindTexture(Texture); Width *= 0.5f; Height *= 0.5f; // 从相机视图矩阵提取世界空间的右向、上向向量 vec3_t cameraRight, cameraUp; Vector(CameraMatrix[0][0], CameraMatrix[1][0], CameraMatrix[2][0], cameraRight); Vector(CameraMatrix[0][1], CameraMatrix[1][1], CameraMatrix[2][1], cameraUp); // 缩放向量到 sprite 尺寸 VectorScale(cameraRight, Width, cameraRight); VectorScale(cameraUp, Height, cameraUp); // 计算四个顶点的世界空间位置 vec3_t p[4]; VectorSubtract(Position, cameraRight, p[0]); VectorSubtract(p[0], cameraUp, p[0]); // 左下 VectorAdd(Position, cameraRight, p[1]); VectorSubtract(p[1], cameraUp, p[1]); // 右下 VectorAdd(Position, cameraRight, p[2]); VectorAdd(p[2], cameraUp, p[2]); // 右上 VectorSubtract(Position, cameraRight, p[3]); VectorAdd(p[3], cameraUp, p[3]); // 左上 // 转换到相机空间 vec3_t pCam[4]; for (int i = 0; i < 4; i++) { VectorTransform(p[i], CameraMatrix, pCam[i]); } glBegin(GL_QUADS); for(int i=0;i<4;i++) { glColor4f(Light[i][0], Light[i][1], Light[i][2], Alpha); glTexCoord2f(UV[i][0], UV[i][1]); glVertex3fv(pCam[i]); } glEnd(); }
2. 调整 RenderNumber 中的文字偏移逻辑
原偏移基于世界空间X轴,改为基于相机右向偏移,确保文字居中:
else { char Text[32]; itoa(Num, Text, 10); unsigned int Length = strlen(Text); // 获取相机右向向量,计算整体偏移让文字居中 vec3_t cameraRight; Vector(CameraMatrix[0][0], CameraMatrix[1][0], CameraMatrix[2][0], cameraRight); VectorScale(cameraRight, -Length * Scale * 0.25f, cameraRight); vec3_t p; VectorCopy(Position, p); VectorAdd(p, cameraRight, p); for(unsigned int i=0;i<Length;i++) { float UV[4][2]; float u = (float)(Text[i]-48)*16.f/256.f; TEXCOORD(UV[0],u ,16.f/32.f); TEXCOORD(UV[1],u+16.f/256.f,16.f/32.f); TEXCOORD(UV[2],u+16.f/256.f,0.f); TEXCOORD(UV[3],u ,0.f); RenderSpriteUV(BITMAP_FONT+1, p, Scale, Scale, UV, Light, Alpha); // 每个字符沿相机右向偏移 vec3_t charOffset; Vector(CameraMatrix[0][0], CameraMatrix[1][0], CameraMatrix[2][0], charOffset); VectorScale(charOffset, Scale * 0.5f, charOffset); VectorAdd(p, charOffset, p); } }
3. 同步修改 RenderSprite 函数
Num=-2分支调用的RenderSprite需做同逻辑修改,确保图标也面向相机:
// 示例修改后的 RenderSprite 函数 void RenderSprite(int Texture, vec3_t Position, float Width, float Height, vec3_t Color, float RotX, float RotY, float RotZ, float UOffset, float VOffset) { BindTexture(Texture); Width *= 0.5f; Height *= 0.5f; vec3_t cameraRight, cameraUp; Vector(CameraMatrix[0][0], CameraMatrix[1][0], CameraMatrix[2][0], cameraRight); Vector(CameraMatrix[0][1], CameraMatrix[1][1], CameraMatrix[2][1], cameraUp); VectorScale(cameraRight, Width, cameraRight); VectorScale(cameraUp, Height, cameraUp); vec3_t p[4]; VectorSubtract(Position, cameraRight, p[0]); VectorSubtract(p[0], cameraUp, p[0]); VectorAdd(Position, cameraRight, p[1]); VectorSubtract(p[1], cameraUp, p[1]); VectorAdd(Position, cameraRight, p[2]); VectorAdd(p[2], cameraUp, p[2]); VectorSubtract(Position, cameraRight, p[3]); VectorAdd(p[3], cameraUp, p[3]); vec3_t pCam[4]; for (int i = 0; i < 4; i++) { VectorTransform(p[i], CameraMatrix, pCam[i]); } // 计算UV(根据原函数的UOffset、VOffset调整) float UV[4][2]; float uStart = UOffset; float uEnd = UOffset + Width*2 / 256.f; // 假设纹理分辨率256,需根据实际调整 float vStart = VOffset; float vEnd = VOffset + Height*2 / 32.f; TEXCOORD(UV[0], uStart, vEnd); TEXCOORD(UV[1], uEnd, vEnd); TEXCOORD(UV[2], uEnd, vStart); TEXCOORD(UV[3], uStart, vStart); glBegin(GL_QUADS); for(int i=0;i<4;i++) { glColor4f(Color[0], Color[1], Color[2], 1.0f); glTexCoord2f(UV[i][0], UV[i][1]); glVertex3fv(pCam[i]); } glEnd(); }
内容的提问来源于stack exchange,提问作者Josef Dvořák
相关产品推荐
相关产品推荐

