NeHe OpenGL第七课:光照和键盘

第七课:光照和键盘

 


 

 

光照和键盘控制:

在这一课里,我们将添加光照和键盘控制,它让程序看起来更美观。

 

这一课我会教您如何使用三种不同的纹理滤波方式。教您如何使用键盘来移动场景中的对象,还会教您在OpenGL场景中应用简单的光照。这一课包含了很多内容,如果您对前面的课程有疑问的话,先回头复习一下。进入后面的代码之前,很好的理解基础知识十分重要。
我们还是在第一课的代码上加以修改。跟以前不一样的是,只要有任何大的改动,我都会写出整段代码。程序开始,我们先加上几个新的变量。

 

下面几行是新的。我们增加三个布尔变量。 light 变量跟踪光照是否打开。变量lp和fp用来存储'L' 和'F'键是否按下的状态。后面我会解释这些变量的重要性。现在,先放在一边吧。

 

BOOL light; // 光源的开/关 
BOOL lp; // L键按下了么? 
BOOL fp; // F键按下了么? 

现在设置5个变量来控制绕x轴和y轴旋转角度的步长,以及绕x轴和y轴的旋转速度。另外还创建了一个z变量来控制进入屏幕深处的距离。

GLfloat xrot;// X 旋转 
GLfloat yrot;// Y 旋转 
GLfloat xspeed;// X 旋转速度 
GLfloat yspeed;// Y 旋转速度 
GLfloat z=-5.0f;// 深入屏幕的距离 

接着设置用来创建光源的数组。我们将使用两种不同的光。第一种称为环境光。环境光来自于四面八方。所有场景中的对象都处于环境光的照射中。第二种类型的光源叫做漫射光。漫射光由特定的光源产生,并在您的场景中的对象表面上产生反射。处于漫射光直接照射下的任何对象表面都变得很亮,而几乎未被照射到的区域就显得要暗一些。这样在我们所创建的木板箱的棱边上就会产生的很不错的阴影效果。
创建光源的过程和颜色的创建完全一致。前三个参数分别是RGB三色分量,最后一个是alpha通道参数。
因此,下面的代码我们得到的是半亮(0.5f)的白色环境光。如果没有环境光,未被漫射光照到的地方会变得十分黑暗。

GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f }; // 环境光参数 

 

下一行代码我们生成最亮的漫射光。所有的参数值都取成最大值1.0f。它将照在我们木板箱的前面,看起来挺好。

 

GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f }; // 漫射光参数

 

最后我们保存光源的位置。前三个参数和glTranslate中的一样。依次分别是XYZ轴上的位移。由于我们想要光线直接照射在木箱的正面,所以XY轴上的位移都是0.0f。第三个值是Z轴上的位移。为了保证光线总在木箱的前面,所以我们将光源的位置朝着观察者(就是您哪。)挪出屏幕。我们通常将屏幕也就是显示器的屏幕玻璃所处的位置称作Z轴的0.0f点。所以Z轴上的位移最后定为2.0f。假如您能够看见光源的话,它就浮在您显示器的前方。当然,如果木箱不在显示器的屏幕玻璃后面的话,您也无法看见箱子。『译者注:我很欣赏NeHe的耐心。说真的有时我都打烦了,这么简单的事他这么废话干嘛?但如果什么都清楚,您还会翻着这样的页面看个没完么?』
最后一个参数取为1.0f。这将告诉OpenGL这里指定的坐标就是光源的位置,以后的教程中我会多加解释。

 

GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };// 光源位置

 

filter 变量跟踪显示时所采用的纹理类型。第一种纹理(texture 0) 使用gl_nearest(不光滑)滤波方式构建。第二种纹理 (texture 1) 使用gl_linear(线性滤波) 方式,离屏幕越近的图像看起来就越光滑。第三种纹理 (texture 2) 使用 mipmapped滤波方式,这将创建一个外观十分优秀的纹理。根据我们的使用类型,filter 变量的值分别等于 0, 1 或 2 。下面我们从第一种纹理开始。
GLuint texture[3] 为三种不同纹理分配储存空间。它们分别位于在 texture[0], texture[1] 和 texture[2]中。

GLuint filter;// 滤波类型 
GLuint texture[3];// 3种纹理的储存空间 

现在载入一个位图,并用它创建三种不同的纹理。这一课使用glaux辅助库来载入位图,因此在编译时您应该确认是否包含了glaux库。我知道Delphi和VC++都包含了glaux库,但别的语言不能保证都有。这里我只对新增的代码做注解。如果您对某行代码有疑问的话,请查看教程六。那一课很详细的解释了载入、创建纹理的内容。
在上一段代码后面及ReSizeGLScene()之前的位置,我们增加了下面的代码。这和第六课中载入位图的代码几乎相同。

  

  这段代码调用前面的代码载入位图,并将其转换成3个纹理。Status 变量跟踪纹理是否已载入并被创建了。

int LoadGLTextures()// 载入位图并转换成纹理 
{ 
       int Status=FALSE;// 状态指示器 
    AUX_RGBImageRec *TextureImage[1];// 创建纹理的存储空间 
    memset(TextureImage,0,sizeof(void *)*1);// 将指针设为NULL 

 

现在载入位图并转换成纹理。TextureImage[0]=LoadBMP("Data/Crate.bmp")调用我们的LoadBMP()函数。Data目录下的Crate.bmp将被载入。如果一切正常,图像数据将存放在TextureImage[0]。Status变量被设为TRUE,我们将开始创建纹理。

// 载入位图,检查有错,或位图不存在的话退出 
if (TextureImage[0]=LoadBMP("Data/Crate.bmp")) 
{ 
      Status=TRUE;// 状态设为 TRUE 

 

现在我们已经将图像数据载入TextureImage[0]。我们将用它来创建3个纹理。下面的行告诉OpenGL我们要创建三个纹理,它们将存放在texture[0], texture[1] 和 texture[2] 中。

 

glGenTextures(3, &texture[0]);// 创建纹理

 

 第六课中我们使用了线性滤波的纹理贴图。这需要机器有相当高的处理能力,但它们看起来很不错。这一课中,我们接着要创建的第一种纹理使用 GL_NEAREST方式。从原理上讲,这种方式没有真正进行滤波。它只占用很小的处理能力,看起来也很差。唯一的好处是这样我们的工程在很快和很慢的机器上都可以正常运行。
您会注意到我们在 MIN 和 MAG 时都采用了GL_NEAREST,你可以混合使用 GL_NEAREST 和 GL_LINEAR。纹理看起来效果会好些,但我们更关心速度,所以全采用低质量贴图。MIN_FILTER在图像绘制时小于贴图的原始尺寸时采用。MAG_FILTER在图像绘制时大于贴图的原始尺寸时采用。

 

// 创建 Nearest 滤波贴图 
glBindTexture(GL_TEXTURE_2D, texture[0]); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); 
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); 

// 创建线性滤波纹理 
glBindTexture(GL_TEXTURE_2D, texture[1]); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); 

 

下面是创建纹理的新方法。您可能会注意到当图像在屏幕上变得很小的时候,很多细节将会丢失。刚才还很不错的图案变得很难看。当您告诉OpenGL创建一个 mipmapped的纹理后,OpenGL将尝试创建不同尺寸的高质量纹理。当您向屏幕绘制一个 mipmapped纹理的时候,OpenGL将选择它已经创建的外观最佳的纹理(带有更多细节)来绘制,而不仅仅是缩放原先的图像(这将导致细节丢失)。
我曾经说过有办法可以绕过OpenGL对纹理宽度和高度所加的限制——64、128、256,等等。办法就是 gluBuild2DMipmaps。据我的发现,您可以使用任意的位图来创建纹理。OpenGL将自动将它缩放到正常的大小。
因为是第三个纹理,我们将它存到texture[2]。这样本课中的三个纹理全都创建好了。 

// 创建 MipMapped 纹理 
glBindTexture(GL_TEXTURE_2D, texture[2]); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); 

  

下面一行生成 mipmapped 纹理。我们使用三种颜色(红,绿,蓝)来生成一个2D纹理。TextureImage[0]->sizeX 是位图宽度,extureImage[0]->sizeY 是位图高度,GL_RGB意味着我们依次使用RGB色彩。GL_UNSIGNED_BYTE 意味着纹理数据的单位是字节。TextureImage[0]->data指向我们创建纹理所用的位图。 

gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); 
} 

现在释放用来存放位图数据的内存。我们先查看位图数据是否存放在 TextureImage[0] 中,如果有,删掉。然后释放位图结构以确保内存被释放。

if (TextureImage[0]) // 纹理是否存在 
{ 
    if (TextureImage[0]->data)// 纹理图像是否存在 
  { 
        free(TextureImage[0]->data); // 释放纹理图像占用的内存 
  } 
   free(TextureImage[0]);// 释放图像结构 
} 

 

最后我们返回 status 变量。如果一切OK,status 变量的值为TRUE。否则为FALSE。

return Status;// 返回 Status 变量 
} 

 

接着应该载入纹理并初始化OpenGL设置了。InitGL函数的第一行使用上面的代码载入纹理。创建纹理之后,我们调用
glEnable(GL_TEXTURE_2D)启用2D纹理映射。阴影模式设为平滑阴影( smooth shading )。背景色设为黑色,我们启用深度测试,然后我们启用优化透视计算。

   现在开始设置光源。下面下面一行设置环境光的发光量,光源light1开始发光。这一课的开始处我们我们将环境光的发光量存放在LightAmbient数组中。现在我们就使用此数组(半亮度环境光)。在int InitGL(GLvoid)函数中添加下面的代码。 

glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // 设置环境光

接下来我们设置漫射光的发光量。它存放在LightDiffuse数组中(全亮度白光)。

 

glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);// 设置漫射光

 

然后设置光源的位置。位置存放在 LightPosition 数组中(正好位于木箱前面的中心,X-0.0f,Y-0.0f,Z方向移向观察者2个单位<位于屏幕外面>)。

glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); // 设置光源位置 

 

最后,我们启用一号光源。我们还没有启用GL_LIGHTING,所以您看不见任何光线。记住:只对光源进行设置、定位、甚至启用,光源都不会工作。除非我们启用GL_LIGHTING。

glEnable(GL_LIGHT1);// 启用一号光源 

 

下一段代码绘制贴图立方体。我只对新增的代码进行注解。如果您对没有注解的代码有疑问,回头看看第六课。

int DrawGLScene(GLvoid)// 从这里开始进行所有的绘制 
{ 
     // 清除屏幕和深度缓存 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
       glLoadIdentity();// 重置当前的模型观察矩阵 

 

下三行代码放置并旋转贴图立方体。glTranslatef(0.0f,0.0f,z)将立方体沿着Z轴移动Z单位。glRotatef(xrot,1.0f,0.0f,0.0f)将立方体绕X轴旋转xrot。glRotatef(yrot,0.0f,1.0f,0.0f)将立方体绕Y轴旋转yrot。

glTranslatef(0.0f,0.0f,z);// 移入/移出屏幕 z 个单位 
glRotatef(xrot,1.0f,0.0f,0.0f);// 绕X轴旋转 
glRotatef(yrot,0.0f,1.0f,0.0f);// 绕Y轴旋转 

 

下一行与我们在第六课中的类似。有所不同的是,这次我们绑定的纹理是texture[filter],而不是上一课中的texture[0]。任何时候,我们按下F键,filter 的值就会增加。如果这个数值大于2,变量filter 将被重置为0。程序初始时,变量filter 的值也将设为0。使用变量filter 我们就可以选择三种纹理中的任意一种。

glBindTexture(GL_TEXTURE_2D, texture[filter]);// 选择由filter决定的纹理 
glBegin(GL_QUADS);// 开始绘制四边形 

  

glNormal3f是这一课的新东西。Normal就是法线的意思,所谓法线是指经过面(多边形)上的一点且垂直于这个面(多边形)的直线。使用光源的时候必须指定一条法线。法线告诉OpenGL这个多边形的朝向,并指明多边形的正面和背面。如果没有指定法线,什么怪事情都可能发生:不该照亮的面被照亮了,多边形的背面也被照亮....。对了,法线应该指向多边形的外侧。

看着木箱的前面您会注意到法线与Z轴正向同向。这意味着法线正指向观察者-您自己。这正是我们所希望的。对于木箱的背面,也正如我们所要的,法线背对着观察者。如果立方体沿着X或Y轴转个180度的话,前侧面的法线仍然朝着观察者,背面的法线也还是背对着观察者。换句话说,不管是哪个面,只要它朝着观察者这个面的法线就指向观察者。由于光源紧邻观察者,任何时候法线对着观察者时,这个面就会被照亮。并且法线越朝着光源,就显得越亮一些。如果您把观察点放到立方体内部,你就会法线里面一片漆黑。因为法线是向外指的。如果立方体内部没有光源的话,当然是一片漆黑。

// 前侧面 
glNormal3f( 0.0f, 0.0f, 1.0f);// 法线指向观察者 
glTexCoord2f(0.0f, 0.0f); 
glVertex3f(-1.0f, -1.0f, 1.0f); 
glTexCoord2f(1.0f, 0.0f); 
glVertex3f( 1.0f, -1.0f, 1.0f); 
glTexCoord2f(1.0f, 1.0f); 
glVertex3f( 1.0f, 1.0f, 1.0f); 
glTexCoord2f(0.0f, 1.0f); 
glVertex3f(-1.0f, 1.0f, 1.0f); 
// 后侧面 
glNormal3f( 0.0f, 0.0f,-1.0f);// 法线背向观察者 
glTexCoord2f(1.0f, 0.0f); 
glVertex3f(-1.0f, -1.0f, -1.0f); 
glTexCoord2f(1.0f, 1.0f); 
glVertex3f(-1.0f, 1.0f, -1.0f); 
glTexCoord2f(0.0f, 1.0f); 
glVertex3f( 1.0f, 1.0f, -1.0f); 
glTexCoord2f(0.0f, 0.0f); 
glVertex3f( 1.0f, -1.0f, -1.0f); 
// 顶面 
glNormal3f( 0.0f, 1.0f, 0.0f);// 法线向上 
glTexCoord2f(0.0f, 1.0f); 
glVertex3f(-1.0f, 1.0f, -1.0f); 
glTexCoord2f(0.0f, 0.0f); 
glVertex3f(-1.0f, 1.0f, 1.0f); 
glTexCoord2f(1.0f, 0.0f); 
glVertex3f( 1.0f, 1.0f, 1.0f); 
glTexCoord2f(1.0f, 1.0f); 
glVertex3f( 1.0f, 1.0f, -1.0f); 
// 底面 
glNormal3f( 0.0f,-1.0f, 0.0f);// 法线朝下 
glTexCoord2f(1.0f, 1.0f); 
glVertex3f(-1.0f, -1.0f, -1.0f); 
glTexCoord2f(0.0f, 1.0f); 
glVertex3f( 1.0f, -1.0f, -1.0f); 
glTexCoord2f(0.0f, 0.0f); 
glVertex3f( 1.0f, -1.0f, 1.0f); 
glTexCoord2f(1.0f, 0.0f); 
glVertex3f(-1.0f, -1.0f, 1.0f); 
// 右侧面 
glNormal3f( 1.0f, 0.0f, 0.0f);// 法线朝右 
glTexCoord2f(1.0f, 0.0f); 
glVertex3f( 1.0f, -1.0f, -1.0f); 
glTexCoord2f(1.0f, 1.0f); 
glVertex3f( 1.0f, 1.0f, -1.0f); 
glTexCoord2f(0.0f, 1.0f); 
glVertex3f( 1.0f, 1.0f, 1.0f); 
glTexCoord2f(0.0f, 0.0f); 
glVertex3f( 1.0f, -1.0f, 1.0f);
 // 左侧面 
glNormal3f(-1.0f, 0.0f, 0.0f);// 法线朝左 
glTexCoord2f(0.0f, 0.0f); 
glVertex3f(-1.0f, -1.0f, -1.0f); 
glTexCoord2f(1.0f, 0.0f); 
glVertex3f(-1.0f, -1.0f, 1.0f); 
glTexCoord2f(1.0f, 1.0f); 
glVertex3f(-1.0f, 1.0f, 1.0f); 
glTexCoord2f(0.0f, 1.0f); 
glVertex3f(-1.0f, 1.0f, -1.0f);
 glEnd();// 四边形绘制结束

 

下两行代码将xot和yrot的旋转值分别增加xspeed和yspeed个单位。xspeed和yspeed的值越大,立方体转得就越快。

xrot+=xspeed;// xrot 增加 xspeed 单位 
yrot+=yspeed;// yrot 增加 yspeed 单位 
return TRUE; 
} 

 

现在转入WinMain()主函数。我们将在这里增加开关光源、旋转木箱、切换过滤方式以及将木箱移近移远的控制代码。在接近WinMain()函数结束的地方你会看到SwapBuffers(hDC)这行代码。然后就在这一行后面添加如下的代码。
代码将检查L键是否按下过。如果L键已按下,但lp的值不是false的话,意味着L键还没有松开,这时什么都不会发生。

SwapBuffers(hDC); // 交换缓存 
if (keys['L'] && !lp)// L 键已按下并且松开了? 
{ 

 

如果lp的值是false的话,意味着L键还没按下,或者已经松开了,接着lp将被设为TRUE。同时检查这两个条件的原因是为了防止L键被按住后,这段代码被反复执行,并导致窗体不停闪烁。
lp设为true之后,计算机就知道L键按过了,我们则据此可以切换光源的开/关:布尔变量light控制光源的开关。

lp=TRUE; // lp 设为 TRUE 
light=!light;// 切换光源的 TRUE/FALSE
if (!light) // 如果没有光源 
{ 
glDisable(GL_LIGHTING);// 禁用光源 
} 
else// 否则 
{ 
glEnable(GL_LIGHTING);// 启用光源 
} 
}

 

 下面的代码查看是否松开了"L"键。如果松开,变量lp将设为false。这意味着"L"键没有按下。如果不作此检查,光源第一次打开之后,就无法再关掉了。计算机会以为"L"键一直按着呢。

if (!keys['L'])// L键松开了么? 
{ 
     lp=FALSE; // 若是,则将lp设为FALSE 
} 

 

然后对"F"键作相似的检查。如果有按下"F"键并且"F"键没有处于按着的状态或者它就从没有按下过,将变量fp设为true。这意味着这个键正被按着呢。接着将filter变量加一。如果filter变量大于2(因为这里我们的使用的数组是texture[3],大于2的纹理不存在),我们重置filter变量为0。

if (keys['F'] && !fp)// F键按下了么? 
{ 
fp=TRUE; // fp 设为 TRUE 
filter+=1; // filter的值加一 
if (filter>2)// 大于2了么? 
{ 
filter=0;// 若是重置为0 
} 
} 
if (!keys['F'])// F键放开了么? 
{
 fp=FALSE;// 若是fp设为FALSE 
}

 

这四行检查是否按下了PageUp键。若是的话,减少z变量的值。这样DrawGLScene函数中包含的glTranslatef(0.0f,0.0f,z)调用将使木箱离观察者更远一点。

if (keys[VK_PRIOR])// PageUp按下了? 
{ 
z-=0.02f;// 若按下,将木箱移向屏幕内部 
} 

 

接着四行检查PageDown键是否按下,若是的话,增加z变量的值。这样DrawGLScene函数中包含的glTranslatef(0.0f,0.0f,z)调用将使木箱向着观察者移近一点。

if (keys[VK_NEXT])// PageDown按下了么 
{ 
z+=0.02f; // 若按下的话,将木箱移向观察者 
}

  

现在检查方向键。按下左右方向键xspeed相应减少或增加。按下上下方向键yspeed相应减少或增加。记住在以后的教程中如果xspeed、yspeed的值增加的话,立方体就转的更快。如果一直按着某个方向键,立方体会在那个方向上转的越快。

if (keys[VK_UP])Up方向键按下了么? 
{ 
xspeed-=0.01f;// 若是,减少xspeed 
} 
if (keys[VK_DOWN]) // Down方向键按下了么?
 { 
xspeed+=0.01f;// 若是,增加xspeed 
} 
if (keys[VK_RIGHT])// Right方向键按下了么? 
{ 
yspeed+=0.01f;// 若是,增加yspeed 
} 
if (keys[VK_LEFT])// Left方向键按下了么? 
{ 
yspeed-=0.01f;// 若是, 减少yspeed 
} 

 

像前几课一样,我们最后还需要更正窗体的标题。

if (keys[VK_F1])// F1按下了么? 
{ 
keys[VK_F1]=FALSE;// 若是将其设为FALSE 
KillGLWindow();// 销毁当前窗口 
fullscreen=!fullscreen;// 切换全屏/窗口模式 
// 重建GL窗口 
if (!CreateGLWindow("NeHe's Textures, Lighting & Keyboard Tutorial",640,480,16,fullscreen)) 
{ 
return 0;// 若无法创建窗口,程序退出 
} 
} 
} 
} 
} 
// 关闭 
KillGLWindow();// 销毁窗口 
return (msg.wParam);// 退出程序 
} 

 

   原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=07       

【Qt OpenGL教程】07:光照和键盘控制 这次教程中,我们将添加光照和键盘控制,它让程序看起来更美观。我将教大家如何使用键盘来移动场景中的对象,还会教大家在OpenGL场景中应用简单的光照,让我们的程序更加视觉效果更好且受我们控制。 阅读详情

相关推荐

NeHe OpenGL课程 网址整理

NeHe OpenGL第一课:OpenGL窗口 NeHe OpenGL第二课:多边形 NeHe OpenGL第三课:颜色渲染 NeHe OpenGL第四课:旋转 NeHe OpenGL第五课:3D空间 NeHe OpenGL第六课:纹理映射 NeHe OpenGL第七课:光照和键盘 NeHe OpenGL第八课:混合 NeHe OpenGL第九课:移动图像 NeHe OpenGL第十课:3D世界 NeHe OpenGL第十一课:飘动的旗帜 NeHe OpenGL第十二课:显示列表 N

shenyi0_0的博客 1258

纹理滤波方式、光源:Jeff Molofee(NeHe) 的 OPENGL 教程-第七课

 <!--font { font-family: Arial; line-height: 180%; font-size: 12pt; margin-top: 2; margin-bottom: 2 }tbody { font-family: font }-->Jeff Molofee

没吃过猪肉,光看见猪跑啦.................... 2867

【Qt OpenGL】Qt Creator中的3D绘图及动画教程

刚刚学习了Qt Creator,发现Qt提供了QtOpenGL模块,对OpenGL做了不错的封装,这使得我们可以很轻松地在Qt程序中使用OpenGL进行绘图渲染。虽然里面还是由不少专业的解释照搬原文的,但还是加入了大量自己的分析。而且Qt中写OpenGL框架上比VC简单太多了,有不少东西都封装优化好了,代码上还是由有很多区别的。当然,其中原教程没解释好的问题我都作了深入的解释,以及一些多余部分解释、代码都被我删掉简化了。

cly116的博客 3万+

计算机图形学(一)——opengl实现三维立方体添加纹理光照与材质、键盘鼠标控制平移旋转和放大缩小

1.实验要求: 用OpenGL和C语言编写一个带纹理和材质的一个立方体的交互式程序。 1)要求生成一个在立方体,并在立方体的六个面上并分别实现不同的纹理映射和材质。纹理的类型为BMP图片纹理,材质类型可以自己设计。例如,三个面上实现纹理映射的头像和生活照,另外三个面上实现材质(金属、塑料等)。 2)可以利用鼠标和键盘进行交互,实现该立方体的旋转、平移和缩放。 2.实验效果图如下: 正常情况下 加...

诶你爱我吗的博客 8832

50多种的图片处理效果_运用OpenGL ES.zip

opengl es shader 图像处理资源。实现很多种效果。有需要的可以下载

一、Abaqus CAE 入门操作(薄膜应力计算)

一、Abaqus CAE 入门操作(薄膜应力计算)

A20802013的博客 4369

JOGL 第5课 纹理(bmp)

底下是第1~5课的 全部代码Eclipse 3.2下测试成功 这课是我根据Pepe & Lizzie’s Adventures第6课Texture mapping 结合第4课写的 1.首先要引入3个文件:BitmapLoader.java 功能:负责读取bmp图片(只对8位和24位)转化为BufferedImage ...

MakerMyself的专栏 399

Qt5.12.12安卓开发环境搭建:Windows下避开SDK版本坑的保姆级教程

本文提供Qt5.12.12在Windows下搭建Android开发环境的详细教程,重点解决SDK版本兼容性问题,特别是cmdline-tools与JDK8的匹配陷阱。通过精准选择组件版本、手动配置路径和修复常见错误,帮助开发者高效完成环境配置,适用于Qt跨平台移动开发。

weixin_33737774的博客 542

Dataiku DSS构建模式解析:从概念验证到生产部署

在企业级数据科学平台中,构建模式是实现项目全生命周期管理的关键框架。Dataiku DSS通过Design、Automation、Visualization、Deployment四种标准模式,将数据项目划分为不同阶段,确保快速迭代与工程严谨性的平衡。Design模式作为敏捷探索的安全区,提供沙箱环境进行数据准备和模型实验;Automation模式则专注于生产流水线的工业化改造,实现自动化调度和监控。这种模式分离不仅涉及功能界面的变化,更包含版本控制、权限管理和资源配置的系统性调整。对于需要处理实时特征管道或

weixin_34342207的博客 317

智能变电站协议系列-1、GOOSE、SV、MMS协议简介及GOOSE示例运行问题(IEC61850)

目前有一个新的研究,关于电网使用的GOOSE、SV、MMS协议,并使用相关库来做一下测试。由于goose、sv、mms等都属于IEC61850,因此,需要对IEC61850相关内容有一些了解,都是国际标准,属于电气自动化的一部分,对于自动化或者电气相关领域有接触的专业应该或多或少有一些了解。IEC61850目标是用于变电站自动化、分布式能源、水力和天然气发电厂、风力发电厂、电动汽车作为分布式能源等领域,目前似乎在变电站自动化方面的推广和使用更加广泛。

逍遥游的博客 2万+

头歌平台-人工智能导论实验(人脸识别)

答案仅供参考,自己动手丰衣足食!!!

qq_52119136的博客 3026

NeHe OpenGL教程 第七课:光照和键盘

转自【翻译】NeHe OpenGL 教程 前言 声明,此NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。 NeHe OpenGL第七课:光照和键盘 光照和键盘控制: 在这一课里,我们将添加光照和键盘控制,它让程序看起来更美观。 ...

weixin_30414305的博客 110

STM32铅酸蓄电池内阻测试仪的交流阻抗测量与数据采集优化

本文深入探讨了基于STM32的铅酸蓄电池内阻测试仪设计,重点解析了交流阻抗测量原理与数据采集优化方案。通过采用1kHz交流小信号注入和数字相敏检波技术,实现对电池欧姆内阻的无损精准测量,并详细介绍了硬件前端调理、ADC过采样及系统标定等关键优化手段,有效提升了测量精度与稳定性。

weixin_29168393的博客 487

NeHe OpenGL教程 第七课:光照和键盘 代码

#include &lt;windows.h&gt; // Windows的头文件 #include &lt;glew.h&gt; // 包含最新的gl.h,glu.h库 #include &lt;glut.h&gt; // 包含OpenGL实用库 #include &lt;stdio.h&gt; // 标准输入/输出库的头文件 #include &lt;glaux.h&gt; ...

u011955875的博客 334

经典蓝牙中的Sniff、Hold与Park模式

若主设备在Sniff Attempt窗口外发送数据,从设备无法接收,需依赖重传机制。移动时立即传输数据。:Sniff Interval=20ms,Attempt=1时隙。:传感器每2秒唤醒一次同步信标,主设备按需唤醒特定传感器采集数据。Slave进入Sniff模式:每20ms唤醒1个时隙监听数据。:设备需要短暂执行高优先级任务(如扫描其他蓝牙设备)。:无数据传输超时后,设备可能自动进入Sniff模式。:主设备通过信标唤醒从设备,或从设备主动请求退出(:平衡功耗与响应速度,适合间歇性数据传输设备。

shishuangquan111的博客 1673

Vibe coding 氛围编程:编程就像聊天一样,低代码开发时代来了?

Vibe coding:编程就像聊天一样,低代码开发时代来了!

Code_流苏:在代码中寻诗意,在实践中觅真知 4803

NeHe+OpenGL教程 第七课 纹理过滤器、光照和键盘控制

在这节课中,我将教你如何使用三种不同的纹理过滤器。我将教你如何使用键盘来移动物体,如何在OpenGL场景中使用简单的光照。这一课包含了很多内容,如果你对前面的课程有疑问的话,先回头复习一下。在你学习下面的知识之前,对基础知识的熟练掌握很重要。 我们再次地修改第一节课中的代码。通

ygc87的专栏 2559

成功解决 OllamaError: status code 400, message: model ‘llama3.2:latest‘ not found 报错

本文针对Ollama更新后出现的OllamaError: status code 400, message: model 'llama3.2:latest' not found报错,分析了其根本原因——2026年1月Ollama将Llama 3.2模型命名从llama3.2:latest改为meta-llama/Llama-3.2:7b-instruct等新格式。解决方案包括:1)使用新模型名称;2)升级Ollama SDK;3)查询确认本地可用模型;4)批量更新旧项目脚本。通过正确适配新命名规则,

博客 1398

OpenGL中的光照和键盘控制

      这一篇基本上是从Nehe的第7课改编而来的,我将他的Win32代码改写为MFC框架下来实现。第一个遇到的问题就是MFC窗口中如何响应键盘消息,搜索了下资料,发现只需要重载PreTranslateMessage函数就可以让窗口监听按键消息了。BOOL COpenGLDemoView::PreTranslateMessage(MSG* pMsg) {    // TODO:

1138

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

上一篇: eWebEditor在IE8,IE7下所有按钮无效之解决办法
下一篇: Android2.0 Release 1 Eclair API变化预览
yarin
博客等级 码龄17年 35粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值