这章加载一个地表图,也就是说画一个平面(长方形),并用一张图(纹理)贴在上面作为模型的停放地。
为了方便使用单独写成一个cpp。
定义一些变量:
GLint DibiaoChangX,DibiaoKuanZ,DibiaoGaoY;//长度,宽度,高度(拼音)
GLuint Dibiaotexture;//纹理ID
加载和设置,放到前面的 init 部分:
//载入地表图
setDibiao(DibiaoChangX,DibiaoKuanZ,me3ds[0].bian.ymin);
//载入地表图
loadDibiaoPic("Wood-cherry.jpg");
绘制放到drawAllModel中:
DrawDibiao();
前面《opengl 显示有纹理的四面体》有一个加载bmp作纹理的函数,bmp图太大,这里用 CLoad3DS.cpp 中的 BuildTexture(char *szPathName, GLuint &texid)函数。
完整的cpp:
//地表绘制(拼音 dibiao.cpp)
#include "CLoad3DS.h"
GLint DibiaoChangX,DibiaoKuanZ,DibiaoGaoY;//长度,宽度,高度(拼音)
GLuint Dibiaotexture;//纹理ID
;
// 读入一个纹理(CLoad3DS.cpp中取出)
int BuildTexture(char *szPathName, GLuint &texid)
{
HDC hdcTemp; // 用于保存位图的 DC
HBITMAP hbmpTemp; // 暂时保存位图
IPicture *pPicture; // 图接口
OLECHAR wszPath[MAX_PATH+1]; // 图片的完整路径 (WCHAR)
char szPath[MAX_PATH+1]; // 图片的完整路径
long lWidth; // 逻辑单元宽度
long lHeight; // 逻辑单元高度
long lWidthPixels; // 以像素为单位的宽度
long lHeightPixels; // 以像素为单位的高度
GLint glMaxTexDim ; // 保持最大纹理大小
if (strstr(szPathName, "http://")) // 如果路径名(szPathName)包含"http://" 则...
{
strcpy(szPath, szPathName); // 将路径名(szPathName)附加到szPath
}
else // 否则...我们就从文件加载
{
WCHAR tempPath[MAX_PATH+1];
GetCurrentDirectory(MAX_PATH, tempPath); // 获取我们的工作目录
int lengthOfMbs = WideCharToMultiByte( CP_ACP, 0, tempPath, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte( CP_ACP, 0, tempPath, -1, szPath, lengthOfMbs, NULL, NULL); //宽字符转为多字符
if(!strstr(szPath, "Data\\3DS"))
{
strcat(szPath, PICPATH); // 在工作目录后面追加"\"
}
else
strcat(szPath, "\\"); //如果已经包含(在命令行)
strcat(szPath, szPathName); // 追加路径名
}
//printf("图片路径:%s\n",szPath);
MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH); // 从ASCII转换为Unicode
HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture);
if(FAILED(hr)) { // 如果加载失败
printf("装入纹理出错,可能是文件 %s 不存在!\n",szPathName);
return FALSE; // 返回假
}
hdcTemp = CreateCompatibleDC(GetDC(0)); // 创建与Windows兼容的设备上下文
if(!hdcTemp) // 创造失败了吗?
{
pPicture->Release(); // 递减图片参考计数
return FALSE; //返回假(失败)
}
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim); // 获取支持的最大纹理大小
pPicture->get_Width(&lWidth); // 获取图宽度(转换为像素)
lWidthPixels = MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540);
pPicture->get_Height(&lHeight); // 获取图高度(转换为像素)
lHeightPixels = MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540);
// 将图像调整为最接近的2次方
if (lWidthPixels <= glMaxTexDim) // 图像宽度是否小于或等于卡限制
lWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f);
else // 否则设置宽度为 "最大功率的两个" 卡可以处理
lWidthPixels = glMaxTexDim;
if (lHeightPixels <= glMaxTexDim) // Is Image Height Greater Than Cards Limit
lHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f);
else // Otherwise Set Height To "Max Power Of Two" That The Card Can Handle
lHeightPixels = glMaxTexDim;
// 创建临时位图
BITMAPINFO bi = {0}; // 我们要的位图类型
DWORD *pBits = 0; // 指向位图位的指针
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // 设定结构尺寸
bi.bmiHeader.biBitCount = 32; // 32 位
bi.bmiHeader.biWidth = lWidthPixels; // Power Of Two Width
bi.bmiHeader.biHeight = lHeightPixels; // 使图像向上(正Y轴)
bi.bmiHeader.biCompression = BI_RGB; // RGB 编码
bi.bmiHeader.biPlanes = 1; // 1 Bitplane
// 以这种方式创建位图允许我们指定颜色深度,并允许我们立即访问位
hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0);
if(!hbmpTemp) // Did Creation Fail?
{
DeleteDC(hdcTemp); // 删除设备描述表(上下文)
pPicture->Release(); // 递减图片参考计数
return FALSE; // Return False (Failure)
}
SelectObject(hdcTemp, hbmpTemp); // 选择处理我们的 temp DC 和我们的 temp 位图对象
// 将图片渲染到位图上
pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0);
// 将BGR转换为RGB格式,并添加255的α值
for(long i = 0; i < lWidthPixels * lHeightPixels; i++) // 循环通过所有像素
{
BYTE* pPixel = (BYTE*)(&pBits[i]); // 获取当前像素
BYTE temp = pPixel[0]; // 将第一种颜色存储在 temp 变量(蓝色)中
pPixel[0] = pPixel[2]; // 将红色值移到正确位置(第一个)
pPixel[2] = temp; // 将 temp 值移到正确的蓝色位置(第3个)
// 这将使任何黑色像素完全透明(如果需要,可以硬编码该值)
if ((pPixel[0]==0) && (pPixel[1]==0) && (pPixel[2]==0)) // 像素是完全黑色的吗?
pPixel[3] = 0; // 将α值设置为0
else // Otherwise
pPixel[3] = 255; // Set The Alpha Value To 255
}
glGenTextures(1, &texid); // 创建纹理
// 使用位图中的数据生成典型纹理
glBindTexture(GL_TEXTURE_2D, texid); // 绑定到纹理id
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // (针对所需的筛选类型修改此选项)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // (针对所需的筛选类型修改此选项)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lWidthPixels, lHeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits); // (Modify This If You Want Mipmaps)
DeleteObject(hbmpTemp); // Delete The Object
DeleteDC(hdcTemp); // Delete The Device Context
pPicture->Release(); // 递减图片参考计数
//printf( "load %s!" , szPath );
return TRUE; // Return True (都很好)
}
//载入地表图
void loadDibiaoPic(char *szPathName)
{
// 读入地表纹理
if(BuildTexture(szPathName, Dibiaotexture)==false)
printf( "加载地表图失败!" );
}
//设置大小和高度
void setDibiao(GLint w,GLint h,GLint zero)
{
DibiaoChangX=w;DibiaoKuanZ=h;DibiaoGaoY=zero;
cout <<"大小和位置:"<< w<<"," <<h<<","<<zero<< '\n'; //
}
//绘制
void DrawDibiao()
{
//半长宽
GLfloat bw=DibiaoChangX/2;
GLfloat bh=DibiaoKuanZ/2;
// 打开纹理映射
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Dibiaotexture);
glBegin(GL_QUADS);
// 给出法向量
glNormal3f(0.0f, 1.0f, 0.0f);//向上
glTexCoord2f(0.0f, 0.0f); glVertex3f(-bw, DibiaoGaoY, -bh);
glTexCoord2f(1.0f, 0.0f); glVertex3f( bw, DibiaoGaoY, -bh);
glTexCoord2f(1.0f, 1.0f); glVertex3f( bw, DibiaoGaoY, bh);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-bw, DibiaoGaoY, bh);
glEnd();
}
效果图:
无光照图为什么会这么暗?只能期待高手了。