【问题标题】:Fill a polygon with a image opengl用图像opengl填充多边形
【发布时间】:2016-07-16 17:56:21
【问题描述】:

http://coliru.stacked-crooked.com/a/400b648e7442eeb3

您可以阅读一段代码:想法是用纹理填充多边形四边形(图像在四边形内重复)。

我希望图像只重复一次:换句话说,将图像作为多边形的背景。我在一个 800x600 的窗口中,我想放置一个台球桌,例如 600x400(在 glvertex3i 定义的四边形中)。它可以是 glvertex2i 并在 2D 中实现,就像 2D 游戏一样。

任何帮助将不胜感激

我还有更好的代码来支持任何 jpg 文件,而不是 loadtexture 和 readjpeg(我认为):

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, &image[0]);

    loaded_textures[filename] = texname;

    return texname;
}

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;
    }

【问题讨论】:

  • 请贴出您代码的相关部分。 ReadJPEG 实际上并不能帮助回答这个问题。显示您的绘图代码。
  • 示例代码在 coliru 中。是在四边形中重复纹理的示例。更新:我更新了具体示例的 coliru 链接(pared.jpg 是带有纹理的 n 图像,在这种情况下是墙......)
  • 将纹理坐标限制在 0.0...1.0 范围内。另外:您应该意识到您使用的是高度过时的 OpenGL 2.0 API。停止这样做并立即使用 OpenGL 4 是个好主意。
  • 我认为上面的 LoadTexture 和 readjpeg 代码比 coliru 中的代码更好(更灵活)。我该怎么做 ?恐怕,但现在选择 opengl 4 不是一个选择......如果我改变我会有什么优势?
  • 看来您的代码库正在使用 OpenGL v1.0。我建议放弃这个并开始学习现代 OpenGL 任何版本的 OpenGL v3.3 或更高版本!在 jpeg 中阅读也对 OpenGL 不友好。您最好使用 TGA 或最好使用 PNG,您可以使用 bmp,但它确实需要一些工作。

标签: c++ eclipse image opengl textures


【解决方案1】:

我注意到您使用的是已弃用的 OpenGL 版本。现在至于您的情况,我是否关心纹理中的 alpha 值对我来说并不明显。还有每个颜色通道有多少字节?了解这一点很重要。

在您的代码的这一部分中,我认为有几点需要改进:

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, &image[0]);

loaded_textures[filename] = texname;

在您致电glGenTextures(1, &amp;texname); 之前,您应该先致电:

glGetError(); // This will clear errors

在您致电glGenTextures(1, &amp;texname);而不是检查 texname 是否有错误后,首选此方法:

GLenum err = glGetError();
if ( err != GL_NO_ERROR ) {
    // Generate Error Here, either log message to file, console or throw an error
}

接下来是您的glBindTexture( GL_TEXTURE_2D, texname );,这很好。现在,对于生成和绑定纹理的 OpenGL 函数,第二个参数是 OpenGL 自动生成的无符号 ID,它将它与给定的文件名相关联。机器读取无符号值以检查多个资源比检查和验证字符串更容易。当应用程序处理 100 个、1000 个甚至 1,000,000 个资源文件时,这也是一种性能提升。

在此之后,您现在正在设置有关 OpenGL 如何处理 mipmap 的参数。如果这是您想要的 mipmapping 行为,那么您的代码的这一部分似乎很好。但是,使用纹理的此函数的调用者无法设置 mipmap 的质量级别。我通常在这里做的是处理不同质量级别的 mipmap 的枚举,这里是我如何处理 mipmap 的 sn-p:

注意:枚举值未在此函数中定义或声明,它将是传递给它的参数。

// This enum I usually have it declared or defined in a CommonStructs header.
enum FilterQuality {
    FILTER_NONE = 1,
    FILTER_GOOD,
    FILTER_BETTER,
    FILTER_BEST
 }; // FilterQuality

 // Now as within side of my function for generating texture files I have something like this for mipmaps:     
if ( texture.generateMipMap ) {
    switch ( texture.filterQuality ) {
        case TextureInfo::FILTER_NONE : {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
            break;
        }
        case TextureInfo::FILTER_GOOD: {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
            break;
        }
        case TextureInfo::FILTER_BEST: {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
            break;
        }
        default: {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
        }
    } // Switch

    if ( openglVersion.x < 3 ) {
        // In OpenGL v3 GL_GENERATE_MIPMAP Is Deprecated, And In 3.1+ It Was Removed
        // So For Those Versions We Use glGenerateMipmap below
        static const unsigned int GL_GENERATE_MIPMAP = 0x8191;
        glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
    }
} else { // No MipMaps
    switch( texture.filterQuality ) {
        case TextureInfo::FILTER_NONE:
        case TextureInfo::FILTER_GOOD: {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
            break;
        }
        default: {
            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); 的调用,因为我从未使用过它,现在这可能是您想要的某种行为,对此我不知道。

设置完 mimpmapping 信息后,您可以在此处设置用于生成纹理的代码中缺少的钳位或环绕重复参数。它们看起来像这样:

bool wrapRepeat; // This variable would not be here inside of the function,
// but would come from this function's definition so that the caller can
// set this parameter or flag to the behavior they want for each texture in use.

// What these two lines of code will do depending on the state of wrapRepeat
// is they will cause the last parameter to be either WRAP in S & T coordinates or
// to clamp to edge in both S & T coordinates.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );

最后,您的代码将纹理加载到内存中,作为参考,这是我在函数中生成纹理的内容:

// Load Texture Into Video Memory
glPixelStorei( GL_UNPACK_ALIGNMENT, texture.hasAlphaChannel ? 4 : 1 );
glTexImage2D( GL_TEXTURE_2D,
    0,
    ( texture.hasAlphaChannel ? GL_RGBA8 : GL_RGB8 ),
    texture.uWidth,
    texture.uHeight,
    0,
    ( texture.hasAlphaChannel ? GL_RGBA : GL_RGB ),
    GL_UNSIGNED_BYTE,
    &texture.vPixelData[0] );

if ( texture.generateMipMap && openglVersion.x >= 3 ) {
    glGenerateMipmap( GL_TEXTURE_2D );
}

这应该可以帮助您解决当前的问题,即您的纹理被包裹而不是被夹住。

现在对于我的项目,我有一个派生的文件处理程序类,它专门读取纹理文件并加载到 TGA 或 PNG 文件中,它的设计方式可以加载到任何其他纹理或图像文件中只要您向该类添加一个函数来解析文件类型。我在渲染中使用的实际图像类型与文件阅读器是分开的。它们与这里的代码是分开的。此代码属于 AssetStorage 类。我的这一类负责存储所有资产并管理它们的内存,仅此而已。这个类不渲染任何东西,但它确实将对象加载到内存和视频内存中。处理所有对象渲染的是我的批处理类和批处理管理器类。现在我的对象都是使用 GLSL 着色器构建的。但是为了向您完整展示这个功能,可以作为我的框架设计方式的参考。

// ----------------------------------------------------------------------------
// add()
// Creates An OpenGL Texture And Returns It's ID Value
// This Can Only Be Called From The Main OpenGL Thread
TextureInfo AssetStorage::add( const Texture& texture, const std::string& strFilename ) {
    if ( INVALID_UNSIGNED != getTextureInfo( strFilename ).uTextureId ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " can not store " << strFilename << " multiple times";
        throw ExceptionHandler( strStream );
    }

    TextureInfo textureInfo;
    textureInfo.hasTransparency = texture.hasAlphaChannel;
    textureInfo.size = glm::uvec2( texture.uWidth, texture.uHeight );

    glGetError(); // Clear Errors

    glGenTextures( 1, &textureInfo.uTextureId );

    GLenum err = glGetError();
    if ( err != GL_NO_ERROR ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " failed glGenTextures with error code 0x" << std::hex << err;
        throw ExceptionHandler( strStream );
    }

    glBindTexture( GL_TEXTURE_2D, textureInfo.uTextureId );

    // Wrap Textures
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );

    const glm::uvec2& openglVersion = s_pSettings->getOpenglVersion();

    if ( texture.generateMipMap ) {
        switch ( texture.filterQuality ) {
            case TextureInfo::FILTER_NONE : {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
                break;
            }
            case TextureInfo::FILTER_GOOD: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
                break;
            }
            case TextureInfo::FILTER_BEST: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
                break;
            }
            default: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
            }
        } // Switch

        if ( openglVersion.x < 3 ) {
            // In OpenGL v3 GL_GENERATE_MIPMAP Is Deprecated, And In 3.1+ It Was Removed
            // So For Those Versions We Use glGenerateMipmap below
            static const unsigned int GL_GENERATE_MIPMAP = 0x8191;
            glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
        }
    } else { // No MipMaps
        switch( texture.filterQuality ) {
            case TextureInfo::FILTER_NONE:
            case TextureInfo::FILTER_GOOD: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
                break;
            }
            default: {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            }
        } 
    }

    // Load Texture Into Video Memory
    glPixelStorei( GL_UNPACK_ALIGNMENT, texture.hasAlphaChannel ? 4 : 1 );
    glTexImage2D( GL_TEXTURE_2D,
        0,
        ( texture.hasAlphaChannel ? GL_RGBA8 : GL_RGB8 ),
        texture.uWidth,
        texture.uHeight,
        0,
        ( texture.hasAlphaChannel ? GL_RGBA : GL_RGB ),
        GL_UNSIGNED_BYTE,
        &texture.vPixelData[0] );

    if ( texture.generateMipMap && openglVersion.x >= 3 ) {
        glGenerateMipmap( GL_TEXTURE_2D );
    }

    // Store TextureId
    BlockThread blockThread( s_criticalSection );
    m_textureInfos.insert( MapTextureInfos::value_type( strFilename, textureInfo ) );

    if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
        Logger::log( std::string( "Created " ) + strFilename );
    }

    return textureInfo;
} // add

【讨论】:

  • 你能在 coliru 中编辑它吗?有一个使用示例会很好..cnilove.com/pool-table-top/… 作为背景...我想在四边形 fo 宽度,高度尺寸中使用它。我假设您正在使用功能(loadtexture 和 readjpege)“改进" 如上所示.. 而不是在 coliru 中使用的其他。谢谢你的帮助。它不是重要的颜色或 alpha 字节,alpha 我总是使用 0(我认为)...我只需要在四边形内显示台球桌...然后我必须添加球等...
  • 另外,如果有更简单的方法,也不需要使用此方法。我的想法是,在例如 800x600 的窗口中,我可以添加黑色作为 glClearColor,然后将台球桌放入例如 400x300 (让边框变黑)。感谢您的帮助...
  • @EduardoGutiérrez 将材质(颜色)应用到多边形与应用纹理是有区别的。
  • @EduardoGutiérrez 基本上你仍然想要创建三角形条带。您必须定义 2 个三角形并以正确的缠绕顺序定义它们的顶点。这将在您的 Image2d 类中。
  • @EduardoGutiérrez 我对大肠杆菌一点也不熟悉。我可以推荐一个很棒的网站,其中包含一些处理 OpenGL 和 3D 游戏引擎开发的视频教程系列。访问MarekKnows.com 他的第一个 OpenGL 游戏引擎系列教程详细介绍了 OpenGL v1.0 如何创建不同的形状、为其应用材质和纹理、如何定义顶点、索引和 UV 坐标。它还展示了如何创建照明。然后他的下一个系列是 OpenGL Tips & Tricks 和 Physics Engine 的组合。然后我们用它在 Ghost Toast 中制作 3D 游戏......
猜你喜欢
  • 1970-01-01
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-05
  • 2019-02-15
  • 2013-05-28
  • 2015-02-03
相关资源
最近更新 更多