加载不同的纹理 Opengl:减慢程序
Load different textures Opengl: slows the program
我正在 3d 中实现迷宫。它工作正常,但现在我必须在其中添加纹理。我喜欢加载不同的纹理,具体取决于它是墙壁、食物还是走廊。我找到了每次需要纹理时调用 LoadTexture 的方法。
我遇到了一个使用函数 Readjpeg 和 LoadTexture 的模板。但是结果太慢了。程序逻辑很好,但是动作很慢。假设因为我从文件中读取了很多次,然后 idle 没有像以前那样被调用。
v
oid Maze::draw3D(int w, int h){
if (rows == 0 || columns == 0)
throw std::out_of_range("Error: El numero columnas o filas no puede ser cero. Error división por cero");
if (rows > h || columns > w)
throw std::out_of_range("Error: La ventana tiene que tener un tamaño mayor a las fila por columnas");
int numberRow, numberColumn;
int widthRatio =int(w / columns);
int heightRatio = int(h / rows);
//int numberRow;
GLUquadric *sphere=gluNewQuadric();
for(numberRow=0;numberRow < rows; numberRow++)
for(numberColumn=0; numberColumn< columns;numberColumn++)
if(!isCenter(numberRow,numberColumn)){
if( map[numberRow][numberColumn]==WALL ) {
//Selecciona el color actual con el que dibujar. Parámetros R G y B, rango [0..1], así que estamos ante el color azul
//glColor3f(0.0, 0.0, 1.0);
/*glBegin() comienza una secuencia de vértices con los que se construirán primitivas. El tipo de primitivas viene dado por el parámetro de glBegin(), en este caso GL_QUADS.
* Al haber cuatro vértices dentro de la estructura, está definiendo un cuadrado. glEnd() simplemente cierra la estructura.
*
*/
//las x son iguales cambian las y
//ysim =(rows-numberRow);
//numberRow=numberRow;
//Pared suelo
glColor3f(1.0, 0.0, 0.0); // Red = rgb <1, 0, 0>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow*heightRatio)-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),0,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),0,(numberRow)*heightRatio-(HEIGHT/2));
glEnd();
/*
//Pared techo
glColor3f(1.0, 0.0, 0.0); // Red = rgb <1, 0, 0>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow*heightRatio)-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow)*heightRatio-(HEIGHT/2));
//glVertex3i(50,-50,50);
glEnd();
*/
//Pared exterior (las x mas izquierda) (1)
glColor3f(0.0, 1.0, 0.0); //Green = rgb <0, 1, 0>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow*heightRatio)-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i( ((numberColumn)*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow*heightRatio)-(HEIGHT/2));
glEnd();
//Pared exterior ( las y mas abajo) (2)
glColor3f(0.0, 0.0, 1.0);//Blue = rgb <0, 0, 1>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i(((numberColumn)*widthRatio)-(WIDTH/2),0, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow+1)*heightRatio-(HEIGHT/2));
glEnd();
//Pared exterior ( las x mas abajo) (3)106;90;205
glColor3f(1.0, 0.5, 0.0); //Orange = color red 1 green 0.5 blue 0.0
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow)*heightRatio-(HEIGHT/2));
glEnd();
//Pared exterior ( las y mas arriba) (4)
glColor3f(0.752, 0.752, 0.752); //Grey = color red 0.752941 green 0.752941 blue 0.752941
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i(((numberColumn)*widthRatio)-(WIDTH/2),DEPTH, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow)*heightRatio-(HEIGHT/2));
glEnd();
//Textura pared techo
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture("Groundplant64x64.jpg",64);
glBegin(GL_QUADS);
glTexCoord2f(-4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow*heightRatio)-(HEIGHT/2));
glTexCoord2f(4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(-4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow)*heightRatio-(HEIGHT/2));
glEnd();
glDisable(GL_TEXTURE_2D);
}
else if(map[numberRow][numberColumn] == FOOD){
//Selecciona el color actual con el que dibujar. Parámetros R G y B, rango [0..1], así que estamos ante el color blanco
glColor3f(1.0,1.0,0.0); //Orange
/*glBegin() comienza una secuencia de vértices con los que se construirán primitivas. El tipo de primitivas viene dado por el parámetro de glBegin(), en este caso GL_QUADS.
* Al haber cuatro vértices dentro de la estructura, está definiendo un cuadrado. glEnd() simplemente cierra la estructura.
*
*/
//GLUquadric *sphere=gluNewQuadric();
gluQuadricDrawStyle( sphere, GLU_FILL);
gluQuadricNormals( sphere, GLU_SMOOTH);
gluQuadricOrientation( sphere, GLU_OUTSIDE);
gluQuadricTexture( sphere, GL_TRUE);
glPushMatrix();
glTranslated(((numberColumn+0.5)*widthRatio)-(WIDTH/2),DEPTH/3,(numberRow+0.5)*heightRatio-(HEIGHT/2) );
//glRotated(45,1,1,1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture("Flames64x64.jpg",64);
gluSphere(sphere,5.0,50,50);//(numberColumn+1)*widthRatio)-(WIDTH/2),0, (ysim)*heightRatio-(HEIGHT/2)
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
else if(map[numberRow][numberColumn] == PASSAGE){
//Selecciona el color actual con el que dibujar. Parámetros R G y B, rango [0..1], así que estamos ante el color blanco
glColor3f(1.0,0.5,0.0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture("FloorsMedieval64x64.jpg",64);
glBegin(GL_QUADS);
glTexCoord2f(-4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),2,(numberRow*heightRatio)-(HEIGHT/2));
glTexCoord2f(4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),2,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),2,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(-4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),2,(numberRow)*heightRatio-(HEIGHT/2));
glEnd();
glDisable(GL_TEXTURE_2D);
}
}
}
我们的老师传给我们以下代码来使用:
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void Maze::ReadJPEG(char *filename,unsigned char **image,int *width, int *height)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * infile;
unsigned char **buffer;
int i,j;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
if ((infile = fopen(filename, "rb")) == NULL) {
printf("Unable to open file %s\n",filename);
exit(1);
}
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
*width = cinfo.output_width;
*height = cinfo.output_height;
*image=(unsigned char*)malloc(cinfo.output_width*cinfo.output_height*cinfo.output_components);
buffer=(unsigned char **)malloc(1*sizeof(unsigned char **));
buffer[0]=(unsigned char *)malloc(cinfo.output_width*cinfo.output_components);
i=0;
while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
for(j=0;j<cinfo.output_width*cinfo.output_components;j++)
{
(*image)[i]=buffer[0][j];
i++;
}
}
free(buffer);
jpeg_finish_decompress(&cinfo);
}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void Maze::LoadTexture(char *filename,int dim)
{
unsigned char *buffer;
unsigned char *buffer2;
int width,height;
long i,j;
long k,h;
ReadJPEG(filename,&buffer,&width,&height);
buffer2=(unsigned char*)malloc(dim*dim*3);
//-- The texture pattern is subsampled so that its dimensions become dim x dim --
for(i=0;i<dim;i++)
for(j=0;j<dim;j++)
{
k=i*height/dim;
h=j*width/dim;
buffer2[3*(i*dim+j)]=buffer[3*(k*width +h)];
buffer2[3*(i*dim+j)+1]=buffer[3*(k*width +h)+1];
buffer2[3*(i*dim+j)+2]=buffer[3*(k*width +h)+2];
}
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,dim,dim,0,GL_RGB,GL_UNSIGNED_BYTE,buffer2);
free(buffer);
free(buffer2);
}
如果有另一种方法可以让我在内存中有不同的纹理(jpeg 中的 64x64 像素图像)或任何提高速度的想法。动画非常非常慢。
谢谢
按照@datenwolf
的建议进行更新
我的readJPEG.cpp
#include "ReadJPEG.h"
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
int ReadJPEG(
std::string const filename,
std::vector<uint8_t> *image,
int *width, int *height )
{
if( !image ) {
return -1;
}
FILE * const infile = fopen(filename, "rb");
if( !infile ) {
std::cerr
<< "error opening file "
<< filename
<< " : "
<< strerror(errno)
<< std::endl;
return -2;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
if( width ) { *width = cinfo.output_width; }
if( height ) { *height = cinfo.output_height; }
size_t const stride = cinfo.output_width * cinfo.output_components;
// image->resize(cinfo.output.height * stride);
image->resize(cinfo.output_height * stride);
jpeg_read_scanlines(&cinfo, &(*image)[0], cinfo.output_height);
jpeg_finish_decompress(&cinfo);
//jpeg_read_scanlines(&cinfo, &(*image)[0], cinfo.output_height);
return 0;
}
MyLoadTexture.cpp
#include "MyLoadTexture.h"
//using namespace std;
GLuint MyLoadTexture(std::string const filename)
{
GLuint texname = 0;
/* this is actually tied to the OpenGL context, so this should
* actually be a map GLcontext -> std::string -> texturename */
static std::map<std::string, GLuint> loaded_textures;
if( loaded_textures.find(filename) != loaded_textures.end() ) {
texname = loaded_textures[filename];
glBindTexture(GL_TEXTURE_2D, texname);
return texname;
}
int width,height;
std::vector<uint8_t> image;
if( ReadJPEG(filename, &image, &width, &height) ) {
std::cerr
<< "error reading JPEG"
<< std::endl;
return 0;
}
glGenTextures(1, &texname);
if( !texname ) {
std::cerr
<< "error generating OpenGL texture name"
<< std::endl;
return 0;
}
glBindTexture(GL_TEXTURE_2D, texname);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
/* glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, buffer );
*/
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, &texname);
loaded_textures[filename] = texname;
return texname;
}
注意我改变了@datewnwolf
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
宽度,高度,0,
GL_RGB,
GL_UNSIGNED_BYTE, 缓冲区);
通过(因为缓冲区不再存在):
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
宽度,高度,0,
GL_RGB,
GL_UNSIGNED_BYTE, &texname);
也是一个词法错误:
图像->调整大小(cinfo.output.height * 步幅);
到
图像->调整大小(cinfo.output_height * 步幅);
我遇到了这个错误:
描述资源路径位置类型
“fopen”未在此范围内声明 ReadJPEG.cpp /RandomMaze3d 第 22 行 C/C++ 问题
描述资源路径位置类型
无法将参数“2”的“unsigned char*”转换为“JSAMPARRAY {aka unsigned char**}”到“JDIMENSION jpeg_read_scanlines(j_decompress_ptr,JSAMPARRAY, JDIMENSION)”ReadJPEG.cpp/RandomMaze3d第 50 行 C/C++ 问题
语义错误(可能与上述错误有关):
描述资源路径位置类型
无效参数 '
候选人是:
_IO_FILE * fopen(常量字符 *, 常量字符 *)
' ReadJPEG.cpp /RandomMaze3d 第 22 行语义错误
描述资源路径位置类型
无效参数 '
候选人是:
unsigned int jpeg_read_scanlines(jpeg_decompress_struct *, unsigned char * *, unsigned int)
' ReadJPEG.cpp /RandomMaze3d 第 50 行语义错误
我也可以使用 int 代替 unint_8 和 String 或 char* 代替 std:string ??不是吗?
更新执行错误:
@datenwolf 非常感谢。我今天早上一直在工作,我可以毫无错误地执行。但是现在我遇到了一个奇怪的执行错误,我在一个地方进行了更改,以使用 MyLoadTexture 和新的 ReadJPEG 作为 LoadTextures 的开头:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
MyLoadTexture("Groundplant64x64.jpg");
glBegin(GL_QUADS); ....
给我一个提示:当我启动 Groundplant64x64,jpg 时,应用程序传输的扫描线太少....文件没有损坏,因为我可以打开它...我用谷歌搜索但没有得到答案。
Groundplants 是 96x64 像素(因为我在保存它时犯了一个错误)但我也尝试过 fire.jpg,它是 64x64。 jpg 保存为 85% quality.I 也曾尝试保存 100% 和同样的错误。
更新执行错误2:
@datenwolf 看到我的更新错误。现在,如果我 运行 我以前的函数 loadTextures 和一个文件,它可以很好地使用 64 作为参数,就像开始时一样。但是,如果我调用 MyLoadTexture,window 就会消失,而不会出现任何错误消息。所以具有所有纹理的地图
静态 std::map loaded_textures;
不应该在MyLoadTexture之外定义为全局变量吗?我想当函数结束时,loaded_textures 消失(它从内存中释放出来)并假设它变成空白。
问题显然在 MyLoadTexture 中,好像我用 LoadTextures("fire.jpg", 64) 代替了一样;一切顺利。然后如果我再次使用 MyLoadTexture("fire.jpg"); window 消失了(试图画迷宫)。
如前所述,MyLoadTexture 和 ReadJPEG 是 public 全局函数,因为我需要从 Maze 以外的 类 调用它们,例如 Ghost、Pacman 类.
请提出其他建议。
LoadTexture
正在经历从磁盘(缓存)读取文件、解析文件、然后加载数据的整个过程。有趣的是,您实际上利用了可追溯到 OpenGL-1.0 的时代错误(已在 1992 年 IIRC 中发布;哎呀,我什至没有关于它的规范),您可以在其中将图像数据加载到纹理中 0
.这样做不是很明智。
相反,只加载所有纹理一次,每个纹理都加载到它自己的纹理对象中。您会看到那里的所有 glBindTexture
个电话。第二个参数是要使用的纹理的 "name"。因此,在加载图像时,您首先要创建 "names",每个要与 glGenTextures
一起使用的图像,然后绑定每个名称,加载图像,然后转到下一张图像。
绘制时,您所要做的只是 glBindTexture
您要使用的纹理,不需要在那里加载任何东西,因为它已经加载了。
根据评论中的要求更新
因此在您的代码中出现了很多这种模式:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture( a_filename );
/* --- draw something --- */
glDisable(GL_TEXTURE_2D);
绑定 OpenGL 纹理 0(允许与 OpenGL-1.0 兼容)并使用 LoadTexture 将图像加载到其中。顺便说一句,LoadTexture 的参数 dim
完全没有必要,实际上 危险 。 JPEG 文件已经包含了您需要的所有信息。
相反,您应该将其替换为如下内容:
先稍微改一下LoadTexture
。将它从迷宫 class 中拉出来,它不属于那里。使它成为一个全局函数。让它生成一个 OpenGL 纹理名称,将纹理加载到其中并 return 纹理名称。同样,所有子采样都是疯狂的; OpenGL 可以处理任意纹理尺寸,只需使用它即可。最后但同样重要的是使用映射将文件名映射到纹理名称,这样已经加载的图像文件就不会重复加载。
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
#include <map>
#include <string>
GLuint LoadTexture(std::string const filename)
{
GLuint texname = 0;
/* this is actually tied to the OpenGL context, so this should
* actually be a map GLcontext -> std::string -> texturename */
static std::map<std::string, GLuint> loaded_textures;
if( loaded_textures.find(filename) != loaded_textures.end() ) {
texname = loaded_textures[filename];
glBindTexture(GL_TEXTURE_2D, texname);
return texname;
}
int width,height;
std::vector<uint8_t> image;
if( ReadJPEG(filename, &image, &width, &height) ) {
std::cerr
<< "error reading JPEG"
<< std::endl;
return 0;
}
glGenTextures(1, &texname);
if( !texname ) {
std::cerr
<< "error generating OpenGL texture name"
<< std::endl;
return 0;
}
glBindTexture(GL_TEXTURE_2D, texname);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, &image[0] );
loaded_textures[filename] = texname;
return texname;
}
应用这些更改后,LoadTexture
将仅加载图像一次,在以后的调用中使用文件名映射到已加载的 OpenGL 纹理,而不是生成新纹理。您可以将它用作直接替换,它的编写方式将 "just work" 与您的其余代码一起使用。
更新 2,ReadJPEG 的改进版本[=76=]
#include <string>
#include <vector>
#include <stdint.h>
#include <string.h>
#include <errno.h>
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
int ReadJPEG(
std::string const filename,
std::vector<uint8_t> *image,
int *width, int *height )
{
if( !image ) {
return -1;
}
FILE * const infile = fopen(filename.c_str(), "rb");
if( !infile ) {
std::cerr
<< "error opening file "
<< filename
<< " : "
<< strerror(errno)
<< std::endl;
return -2;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
if( width ) { *width = cinfo.output_width; }
if( height ) { *height = cinfo.output_height; }
size_t const stride = cinfo.output_width * cinfo.output_components;
image->resize(cinfo.output.height * stride);
for(size_t i = 0; i < cinfo.output_height;) {
uint8_t * const row = &(*image)[stride * i];
i += jpeg_read_scanlines(&cinfo, (unsigned char**)&row, 1);
}
jpeg_finish_decompress(&cinfo);
fclose(infile);
return 0;
}
请注意,此新版本在合理的情况下使用 RAII 和适当的 C++ 习语;我强烈建议不要使用引用,因为引用是伪装的指针,可能会误导你。真实、显式的指针可以防止您陷入此类陷阱。
更新 3 由于问题更新:
Description Resource Path Location Type ‘fopen’ was not declared in this scope ReadJPEG.cpp /RandomMaze3d line 22 C/C++ Problem
在 ReadJPEG.cpp 的顶部添加一个 #include <stdio.h>
– 我也使用 iostream
来发出错误消息,所以也在那里添加 #include <iostream>
,在 LoadTexture.cpp 为了好的措施。
Description Resource Path Location Type cannot convert ‘unsigned char*’ to ‘JSAMPARRAY {aka unsigned char**}’ for argument ‘2’ to ‘JDIMENSION jpeg_read_scanlines(j_decompress_ptr, JSAMPARRAY, JDIMENSION)’ ReadJPEG.cpp /RandomMaze3d line 50 C/C++ Problem
(…)
Description Resource Path Location Type Invalid arguments ' Candidates are: unsigned int jpeg_read_scanlines(jpeg_decompress_struct *, unsigned char * *, unsigned int) ' ReadJPEG.cpp /RandomMaze3d line 50 Semantic Error
查看我编辑的 ReadJPEG 版本(天哪,谁使用双指针间接设计了 libjpeg API)。无论如何,没有什么是每条扫描线的循环和辅助指针变量无法修复的。
Semantic errors ( perhaps related with above errors): Description Resource Path Location Type Invalid arguments ' Candidates are: _IO_FILE * fopen(const char *, const char *) ' ReadJPEG.cpp /RandomMaze3d line 22 Semantic Error
另请参阅我编辑的 ReadJPEG 版本
Also i could save use int instead of unint_8
没有!!!
一个 sizeof(int) != sizeof(uint8_t)
。虽然您可以用 uint8_t
代替 unsigned char
,但是 char
的大小并不是一成不变的(通常是 8 位,但并非总是如此)。有点可耻的是,libjpeg API 使用 unsigned char
作为数据类型,而不是像 uint8_t
这样的真正固定大小的数据类型,它在所有情况下都是 8 位大小。
and String or char* instead of std:string ?? Isnt it ?
我不知道您指的是哪种 String
类型。但是 从不 在 C++ 代码中使用 C 风格的 char*
"strings",除非您与纯 C API 交互。哎呀,我在很大程度上更喜欢 C 而不是 C++,即使在那里我也尽可能远离 char*
并使用一些字符串抽象,如 uStr 或类似的。裸露的 char*
琴弦非常危险。
C++ 有一个标准(作为语言标准库的一部分)字符串类型。它叫做 std::string
,如果你正在编写 C++ 代码,你就可以使用它。这个没有争议。
同时保持 C++ 标准库类型将为您省去显式分配和删除内存的所有麻烦。所有这些血淋淋的细节都很好地隐藏在 STL 的分配器中,如果您虔诚地坚持 RAII(= 从不明确使用 new
或 delete
运算符),您将永远不会遇到内存泄漏;当然你不能总是坚持RAII。
我正在 3d 中实现迷宫。它工作正常,但现在我必须在其中添加纹理。我喜欢加载不同的纹理,具体取决于它是墙壁、食物还是走廊。我找到了每次需要纹理时调用 LoadTexture 的方法。
我遇到了一个使用函数 Readjpeg 和 LoadTexture 的模板。但是结果太慢了。程序逻辑很好,但是动作很慢。假设因为我从文件中读取了很多次,然后 idle 没有像以前那样被调用。
v
oid Maze::draw3D(int w, int h){
if (rows == 0 || columns == 0)
throw std::out_of_range("Error: El numero columnas o filas no puede ser cero. Error división por cero");
if (rows > h || columns > w)
throw std::out_of_range("Error: La ventana tiene que tener un tamaño mayor a las fila por columnas");
int numberRow, numberColumn;
int widthRatio =int(w / columns);
int heightRatio = int(h / rows);
//int numberRow;
GLUquadric *sphere=gluNewQuadric();
for(numberRow=0;numberRow < rows; numberRow++)
for(numberColumn=0; numberColumn< columns;numberColumn++)
if(!isCenter(numberRow,numberColumn)){
if( map[numberRow][numberColumn]==WALL ) {
//Selecciona el color actual con el que dibujar. Parámetros R G y B, rango [0..1], así que estamos ante el color azul
//glColor3f(0.0, 0.0, 1.0);
/*glBegin() comienza una secuencia de vértices con los que se construirán primitivas. El tipo de primitivas viene dado por el parámetro de glBegin(), en este caso GL_QUADS.
* Al haber cuatro vértices dentro de la estructura, está definiendo un cuadrado. glEnd() simplemente cierra la estructura.
*
*/
//las x son iguales cambian las y
//ysim =(rows-numberRow);
//numberRow=numberRow;
//Pared suelo
glColor3f(1.0, 0.0, 0.0); // Red = rgb <1, 0, 0>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow*heightRatio)-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),0,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),0,(numberRow)*heightRatio-(HEIGHT/2));
glEnd();
/*
//Pared techo
glColor3f(1.0, 0.0, 0.0); // Red = rgb <1, 0, 0>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow*heightRatio)-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow)*heightRatio-(HEIGHT/2));
//glVertex3i(50,-50,50);
glEnd();
*/
//Pared exterior (las x mas izquierda) (1)
glColor3f(0.0, 1.0, 0.0); //Green = rgb <0, 1, 0>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow*heightRatio)-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i( ((numberColumn)*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow*heightRatio)-(HEIGHT/2));
glEnd();
//Pared exterior ( las y mas abajo) (2)
glColor3f(0.0, 0.0, 1.0);//Blue = rgb <0, 0, 1>
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i(((numberColumn)*widthRatio)-(WIDTH/2),0, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow+1)*heightRatio-(HEIGHT/2));
glEnd();
//Pared exterior ( las x mas abajo) (3)106;90;205
glColor3f(1.0, 0.5, 0.0); //Orange = color red 1 green 0.5 blue 0.0
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow+1)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow)*heightRatio-(HEIGHT/2));
glEnd();
//Pared exterior ( las y mas arriba) (4)
glColor3f(0.752, 0.752, 0.752); //Grey = color red 0.752941 green 0.752941 blue 0.752941
glBegin(GL_QUADS);
//Vertice arriba izquierda
glVertex3i((numberColumn*widthRatio)-(WIDTH/2),0, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice abajo izquierda
glVertex3i(((numberColumn)*widthRatio)-(WIDTH/2),DEPTH, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice abajo derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),DEPTH, (numberRow)*heightRatio-(HEIGHT/2));
//Vertice arriba derecha
glVertex3i(((numberColumn+1)*widthRatio)-(WIDTH/2),0, (numberRow)*heightRatio-(HEIGHT/2));
glEnd();
//Textura pared techo
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture("Groundplant64x64.jpg",64);
glBegin(GL_QUADS);
glTexCoord2f(-4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow*heightRatio)-(HEIGHT/2));
glTexCoord2f(4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(-4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),DEPTH,(numberRow)*heightRatio-(HEIGHT/2));
glEnd();
glDisable(GL_TEXTURE_2D);
}
else if(map[numberRow][numberColumn] == FOOD){
//Selecciona el color actual con el que dibujar. Parámetros R G y B, rango [0..1], así que estamos ante el color blanco
glColor3f(1.0,1.0,0.0); //Orange
/*glBegin() comienza una secuencia de vértices con los que se construirán primitivas. El tipo de primitivas viene dado por el parámetro de glBegin(), en este caso GL_QUADS.
* Al haber cuatro vértices dentro de la estructura, está definiendo un cuadrado. glEnd() simplemente cierra la estructura.
*
*/
//GLUquadric *sphere=gluNewQuadric();
gluQuadricDrawStyle( sphere, GLU_FILL);
gluQuadricNormals( sphere, GLU_SMOOTH);
gluQuadricOrientation( sphere, GLU_OUTSIDE);
gluQuadricTexture( sphere, GL_TRUE);
glPushMatrix();
glTranslated(((numberColumn+0.5)*widthRatio)-(WIDTH/2),DEPTH/3,(numberRow+0.5)*heightRatio-(HEIGHT/2) );
//glRotated(45,1,1,1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture("Flames64x64.jpg",64);
gluSphere(sphere,5.0,50,50);//(numberColumn+1)*widthRatio)-(WIDTH/2),0, (ysim)*heightRatio-(HEIGHT/2)
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
else if(map[numberRow][numberColumn] == PASSAGE){
//Selecciona el color actual con el que dibujar. Parámetros R G y B, rango [0..1], así que estamos ante el color blanco
glColor3f(1.0,0.5,0.0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture("FloorsMedieval64x64.jpg",64);
glBegin(GL_QUADS);
glTexCoord2f(-4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),2,(numberRow*heightRatio)-(HEIGHT/2));
glTexCoord2f(4.0,0.0); glVertex3i((numberColumn*widthRatio)-(WIDTH/2),2,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),2,(numberRow+1)*heightRatio-(HEIGHT/2));
glTexCoord2f(-4.0,4.0); glVertex3i((numberColumn+1)*widthRatio-(WIDTH/2),2,(numberRow)*heightRatio-(HEIGHT/2));
glEnd();
glDisable(GL_TEXTURE_2D);
}
}
}
我们的老师传给我们以下代码来使用:
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void Maze::ReadJPEG(char *filename,unsigned char **image,int *width, int *height)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * infile;
unsigned char **buffer;
int i,j;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
if ((infile = fopen(filename, "rb")) == NULL) {
printf("Unable to open file %s\n",filename);
exit(1);
}
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
*width = cinfo.output_width;
*height = cinfo.output_height;
*image=(unsigned char*)malloc(cinfo.output_width*cinfo.output_height*cinfo.output_components);
buffer=(unsigned char **)malloc(1*sizeof(unsigned char **));
buffer[0]=(unsigned char *)malloc(cinfo.output_width*cinfo.output_components);
i=0;
while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
for(j=0;j<cinfo.output_width*cinfo.output_components;j++)
{
(*image)[i]=buffer[0][j];
i++;
}
}
free(buffer);
jpeg_finish_decompress(&cinfo);
}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void Maze::LoadTexture(char *filename,int dim)
{
unsigned char *buffer;
unsigned char *buffer2;
int width,height;
long i,j;
long k,h;
ReadJPEG(filename,&buffer,&width,&height);
buffer2=(unsigned char*)malloc(dim*dim*3);
//-- The texture pattern is subsampled so that its dimensions become dim x dim --
for(i=0;i<dim;i++)
for(j=0;j<dim;j++)
{
k=i*height/dim;
h=j*width/dim;
buffer2[3*(i*dim+j)]=buffer[3*(k*width +h)];
buffer2[3*(i*dim+j)+1]=buffer[3*(k*width +h)+1];
buffer2[3*(i*dim+j)+2]=buffer[3*(k*width +h)+2];
}
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,dim,dim,0,GL_RGB,GL_UNSIGNED_BYTE,buffer2);
free(buffer);
free(buffer2);
}
如果有另一种方法可以让我在内存中有不同的纹理(jpeg 中的 64x64 像素图像)或任何提高速度的想法。动画非常非常慢。
谢谢
按照@datenwolf
的建议进行更新我的readJPEG.cpp
#include "ReadJPEG.h"
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
int ReadJPEG(
std::string const filename,
std::vector<uint8_t> *image,
int *width, int *height )
{
if( !image ) {
return -1;
}
FILE * const infile = fopen(filename, "rb");
if( !infile ) {
std::cerr
<< "error opening file "
<< filename
<< " : "
<< strerror(errno)
<< std::endl;
return -2;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
if( width ) { *width = cinfo.output_width; }
if( height ) { *height = cinfo.output_height; }
size_t const stride = cinfo.output_width * cinfo.output_components;
// image->resize(cinfo.output.height * stride);
image->resize(cinfo.output_height * stride);
jpeg_read_scanlines(&cinfo, &(*image)[0], cinfo.output_height);
jpeg_finish_decompress(&cinfo);
//jpeg_read_scanlines(&cinfo, &(*image)[0], cinfo.output_height);
return 0;
}
MyLoadTexture.cpp
#include "MyLoadTexture.h"
//using namespace std;
GLuint MyLoadTexture(std::string const filename)
{
GLuint texname = 0;
/* this is actually tied to the OpenGL context, so this should
* actually be a map GLcontext -> std::string -> texturename */
static std::map<std::string, GLuint> loaded_textures;
if( loaded_textures.find(filename) != loaded_textures.end() ) {
texname = loaded_textures[filename];
glBindTexture(GL_TEXTURE_2D, texname);
return texname;
}
int width,height;
std::vector<uint8_t> image;
if( ReadJPEG(filename, &image, &width, &height) ) {
std::cerr
<< "error reading JPEG"
<< std::endl;
return 0;
}
glGenTextures(1, &texname);
if( !texname ) {
std::cerr
<< "error generating OpenGL texture name"
<< std::endl;
return 0;
}
glBindTexture(GL_TEXTURE_2D, texname);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
/* glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, buffer );
*/
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, &texname);
loaded_textures[filename] = texname;
return texname;
}
注意我改变了@datewnwolf
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
宽度,高度,0,
GL_RGB,
GL_UNSIGNED_BYTE, 缓冲区);
通过(因为缓冲区不再存在):
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
宽度,高度,0,
GL_RGB,
GL_UNSIGNED_BYTE, &texname);
也是一个词法错误: 图像->调整大小(cinfo.output.height * 步幅); 到 图像->调整大小(cinfo.output_height * 步幅);
我遇到了这个错误:
描述资源路径位置类型 “fopen”未在此范围内声明 ReadJPEG.cpp /RandomMaze3d 第 22 行 C/C++ 问题
描述资源路径位置类型 无法将参数“2”的“unsigned char*”转换为“JSAMPARRAY {aka unsigned char**}”到“JDIMENSION jpeg_read_scanlines(j_decompress_ptr,JSAMPARRAY, JDIMENSION)”ReadJPEG.cpp/RandomMaze3d第 50 行 C/C++ 问题
语义错误(可能与上述错误有关): 描述资源路径位置类型 无效参数 ' 候选人是: _IO_FILE * fopen(常量字符 *, 常量字符 *) ' ReadJPEG.cpp /RandomMaze3d 第 22 行语义错误
描述资源路径位置类型 无效参数 ' 候选人是: unsigned int jpeg_read_scanlines(jpeg_decompress_struct *, unsigned char * *, unsigned int) ' ReadJPEG.cpp /RandomMaze3d 第 50 行语义错误
我也可以使用 int 代替 unint_8 和 String 或 char* 代替 std:string ??不是吗?
更新执行错误:
@datenwolf 非常感谢。我今天早上一直在工作,我可以毫无错误地执行。但是现在我遇到了一个奇怪的执行错误,我在一个地方进行了更改,以使用 MyLoadTexture 和新的 ReadJPEG 作为 LoadTextures 的开头:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
MyLoadTexture("Groundplant64x64.jpg");
glBegin(GL_QUADS); ....
给我一个提示:当我启动 Groundplant64x64,jpg 时,应用程序传输的扫描线太少....文件没有损坏,因为我可以打开它...我用谷歌搜索但没有得到答案。
Groundplants 是 96x64 像素(因为我在保存它时犯了一个错误)但我也尝试过 fire.jpg,它是 64x64。 jpg 保存为 85% quality.I 也曾尝试保存 100% 和同样的错误。
更新执行错误2:
@datenwolf 看到我的更新错误。现在,如果我 运行 我以前的函数 loadTextures 和一个文件,它可以很好地使用 64 作为参数,就像开始时一样。但是,如果我调用 MyLoadTexture,window 就会消失,而不会出现任何错误消息。所以具有所有纹理的地图
静态 std::map loaded_textures;
不应该在MyLoadTexture之外定义为全局变量吗?我想当函数结束时,loaded_textures 消失(它从内存中释放出来)并假设它变成空白。
问题显然在 MyLoadTexture 中,好像我用 LoadTextures("fire.jpg", 64) 代替了一样;一切顺利。然后如果我再次使用 MyLoadTexture("fire.jpg"); window 消失了(试图画迷宫)。
如前所述,MyLoadTexture 和 ReadJPEG 是 public 全局函数,因为我需要从 Maze 以外的 类 调用它们,例如 Ghost、Pacman 类.
请提出其他建议。
LoadTexture
正在经历从磁盘(缓存)读取文件、解析文件、然后加载数据的整个过程。有趣的是,您实际上利用了可追溯到 OpenGL-1.0 的时代错误(已在 1992 年 IIRC 中发布;哎呀,我什至没有关于它的规范),您可以在其中将图像数据加载到纹理中 0
.这样做不是很明智。
相反,只加载所有纹理一次,每个纹理都加载到它自己的纹理对象中。您会看到那里的所有 glBindTexture
个电话。第二个参数是要使用的纹理的 "name"。因此,在加载图像时,您首先要创建 "names",每个要与 glGenTextures
一起使用的图像,然后绑定每个名称,加载图像,然后转到下一张图像。
绘制时,您所要做的只是 glBindTexture
您要使用的纹理,不需要在那里加载任何东西,因为它已经加载了。
根据评论中的要求更新
因此在您的代码中出现了很多这种模式:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);
LoadTexture( a_filename );
/* --- draw something --- */
glDisable(GL_TEXTURE_2D);
绑定 OpenGL 纹理 0(允许与 OpenGL-1.0 兼容)并使用 LoadTexture 将图像加载到其中。顺便说一句,LoadTexture 的参数 dim
完全没有必要,实际上 危险 。 JPEG 文件已经包含了您需要的所有信息。
相反,您应该将其替换为如下内容:
先稍微改一下LoadTexture
。将它从迷宫 class 中拉出来,它不属于那里。使它成为一个全局函数。让它生成一个 OpenGL 纹理名称,将纹理加载到其中并 return 纹理名称。同样,所有子采样都是疯狂的; OpenGL 可以处理任意纹理尺寸,只需使用它即可。最后但同样重要的是使用映射将文件名映射到纹理名称,这样已经加载的图像文件就不会重复加载。
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
#include <map>
#include <string>
GLuint LoadTexture(std::string const filename)
{
GLuint texname = 0;
/* this is actually tied to the OpenGL context, so this should
* actually be a map GLcontext -> std::string -> texturename */
static std::map<std::string, GLuint> loaded_textures;
if( loaded_textures.find(filename) != loaded_textures.end() ) {
texname = loaded_textures[filename];
glBindTexture(GL_TEXTURE_2D, texname);
return texname;
}
int width,height;
std::vector<uint8_t> image;
if( ReadJPEG(filename, &image, &width, &height) ) {
std::cerr
<< "error reading JPEG"
<< std::endl;
return 0;
}
glGenTextures(1, &texname);
if( !texname ) {
std::cerr
<< "error generating OpenGL texture name"
<< std::endl;
return 0;
}
glBindTexture(GL_TEXTURE_2D, texname);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, &image[0] );
loaded_textures[filename] = texname;
return texname;
}
应用这些更改后,LoadTexture
将仅加载图像一次,在以后的调用中使用文件名映射到已加载的 OpenGL 纹理,而不是生成新纹理。您可以将它用作直接替换,它的编写方式将 "just work" 与您的其余代码一起使用。
更新 2,ReadJPEG 的改进版本[=76=]
#include <string>
#include <vector>
#include <stdint.h>
#include <string.h>
#include <errno.h>
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
int ReadJPEG(
std::string const filename,
std::vector<uint8_t> *image,
int *width, int *height )
{
if( !image ) {
return -1;
}
FILE * const infile = fopen(filename.c_str(), "rb");
if( !infile ) {
std::cerr
<< "error opening file "
<< filename
<< " : "
<< strerror(errno)
<< std::endl;
return -2;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
if( width ) { *width = cinfo.output_width; }
if( height ) { *height = cinfo.output_height; }
size_t const stride = cinfo.output_width * cinfo.output_components;
image->resize(cinfo.output.height * stride);
for(size_t i = 0; i < cinfo.output_height;) {
uint8_t * const row = &(*image)[stride * i];
i += jpeg_read_scanlines(&cinfo, (unsigned char**)&row, 1);
}
jpeg_finish_decompress(&cinfo);
fclose(infile);
return 0;
}
#include <string>
#include <vector>
#include <stdint.h>
#include <string.h>
#include <errno.h>
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
int ReadJPEG(
std::string const filename,
std::vector<uint8_t> *image,
int *width, int *height )
{
if( !image ) {
return -1;
}
FILE * const infile = fopen(filename.c_str(), "rb");
if( !infile ) {
std::cerr
<< "error opening file "
<< filename
<< " : "
<< strerror(errno)
<< std::endl;
return -2;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
jpeg_start_decompress(&cinfo);
if( width ) { *width = cinfo.output_width; }
if( height ) { *height = cinfo.output_height; }
size_t const stride = cinfo.output_width * cinfo.output_components;
image->resize(cinfo.output.height * stride);
for(size_t i = 0; i < cinfo.output_height;) {
uint8_t * const row = &(*image)[stride * i];
i += jpeg_read_scanlines(&cinfo, (unsigned char**)&row, 1);
}
jpeg_finish_decompress(&cinfo);
fclose(infile);
return 0;
}
请注意,此新版本在合理的情况下使用 RAII 和适当的 C++ 习语;我强烈建议不要使用引用,因为引用是伪装的指针,可能会误导你。真实、显式的指针可以防止您陷入此类陷阱。
更新 3 由于问题更新:
Description Resource Path Location Type ‘fopen’ was not declared in this scope ReadJPEG.cpp /RandomMaze3d line 22 C/C++ Problem
在 ReadJPEG.cpp 的顶部添加一个 #include <stdio.h>
– 我也使用 iostream
来发出错误消息,所以也在那里添加 #include <iostream>
,在 LoadTexture.cpp 为了好的措施。
Description Resource Path Location Type cannot convert ‘unsigned char*’ to ‘JSAMPARRAY {aka unsigned char**}’ for argument ‘2’ to ‘JDIMENSION jpeg_read_scanlines(j_decompress_ptr, JSAMPARRAY, JDIMENSION)’ ReadJPEG.cpp /RandomMaze3d line 50 C/C++ Problem (…) Description Resource Path Location Type Invalid arguments ' Candidates are: unsigned int jpeg_read_scanlines(jpeg_decompress_struct *, unsigned char * *, unsigned int) ' ReadJPEG.cpp /RandomMaze3d line 50 Semantic Error
查看我编辑的 ReadJPEG 版本(天哪,谁使用双指针间接设计了 libjpeg API)。无论如何,没有什么是每条扫描线的循环和辅助指针变量无法修复的。
Semantic errors ( perhaps related with above errors): Description Resource Path Location Type Invalid arguments ' Candidates are: _IO_FILE * fopen(const char *, const char *) ' ReadJPEG.cpp /RandomMaze3d line 22 Semantic Error
另请参阅我编辑的 ReadJPEG 版本
Also i could save use int instead of unint_8
没有!!!
一个 sizeof(int) != sizeof(uint8_t)
。虽然您可以用 uint8_t
代替 unsigned char
,但是 char
的大小并不是一成不变的(通常是 8 位,但并非总是如此)。有点可耻的是,libjpeg API 使用 unsigned char
作为数据类型,而不是像 uint8_t
这样的真正固定大小的数据类型,它在所有情况下都是 8 位大小。
and String or char* instead of std:string ?? Isnt it ?
我不知道您指的是哪种 String
类型。但是 从不 在 C++ 代码中使用 C 风格的 char*
"strings",除非您与纯 C API 交互。哎呀,我在很大程度上更喜欢 C 而不是 C++,即使在那里我也尽可能远离 char*
并使用一些字符串抽象,如 uStr 或类似的。裸露的 char*
琴弦非常危险。
C++ 有一个标准(作为语言标准库的一部分)字符串类型。它叫做 std::string
,如果你正在编写 C++ 代码,你就可以使用它。这个没有争议。
同时保持 C++ 标准库类型将为您省去显式分配和删除内存的所有麻烦。所有这些血淋淋的细节都很好地隐藏在 STL 的分配器中,如果您虔诚地坚持 RAII(= 从不明确使用 new
或 delete
运算符),您将永远不会遇到内存泄漏;当然你不能总是坚持RAII。