接近 1 的纹理坐标表现得很奇怪 [英] Texture coordinates near 1 behave oddly

查看:34
本文介绍了接近 1 的纹理坐标表现得很奇怪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 (Py)OpenGL 显示 256 色索引图像.我将着色器与包含调色板的一维纹理一起使用.这是片段着色器代码:

I'm using (Py)OpenGL to display 256 colors indexed images. I use a shader together with a 1D texture containing the palette. Here's the Fragment shader code :

#version 330
uniform sampler2D texture; 
uniform sampler1D palette;

void main() 
{ 
    vec2 uv = gl_TexCoord[0].xy; 
    vec4 color = texture2D(texture, uv); 
    gl_FragColor = texture1D(palette, color.a) ;
}

为避免舍入错误,所有 MAG 和 MIN 过滤器都设置为 NEAREST.

To avoid rounding errors, all MAG and MIN filters are set to NEAREST.

我看到一维纹理的纹理坐标的方式是:

The way I was seeing the texture coordinates for the 1D texture was :

  • 颜色 0 位于区间 [0 ;1/256[
  • 颜色 1 位于区间 [1/256 ;2/256[...
  • 颜色 255 位于区间 [255/256 ;1[

我将自己的整数索引转换为在 0 和 1 之间浮动,以确保发生了什么,使用公式 x_float = (x_int + .4)/256,即 x_float 位于前面提到的区间内,比它稍早一点居中(避免结果在区间的错误一侧四舍五入).

I converted myself integer indexes to float between 0 and 1, to make sure what was happening, using the formula x_float = (x_int + .4)/256, that is x_float is inside the before mentioned intervals, a little before its center (to avoid the result being rounded at the wrong side of the interval).

但它不起作用.我制作了一个由 256 个单元格组成的棋盘格,颜色索引为 0 到 255,还有一个灰度调色板(从 0x000000 到 0xFFFFFF).代码如下.然后我制作了一个屏幕快照并在 Paint.NET 中对其进行了编辑以查看颜色是否正确,并注意到颜色 0xE0 处有一个跳跃:我得到了两次颜色 0xDF,从这个颜色开始,所有内容都移动了一个:最后一种颜色是 0xFE0xFF.

But it doesn't work. I made a checkboard of 256 celles, with colors indexes 0 to 255 and a palette of levels of grey (from 0x000000 to 0xFFFFFF). The code is given below. I then made a screen snapshot and edited it in Paint.NET to look if colors were right, and noticed a jump at color 0xE0 : I get twice color 0xDF, and from this one, everything is shifted by one : last color is 0xFE instead of 0xFF.

我怀疑存在某种四舍五入错误,但不知道如何...

I suspect some kind of rounding error, but don't see how...

完整代码如下:

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

from OpenGL.arrays import vbo
from OpenGL.GL import shaders
from numpy import *

def checkboard(size = 512, cell = 32):
    bitmap = zeros(size * size, 'u8')
    bitmap.shape = (size, size)
    current_color = 0

    for y in range(0, size, cell):
        for x in range(0, size, cell):
            bitmap[y : y + cell, x : x + cell] = current_color
            current_color += 1

    palette = array([[a, a, a, 255] for a in range(256)], 'u8')

    return bitmap, palette

def reshape(w, h):
    glutDisplayFunc(lambda: display(w, h))
    glutPostRedisplay();

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
glutCreateWindow("Hello World :'D")

### init code

# vbo
quad = (0.0, 0.0, 512.0, 512.0)
tex = (0., 0., 1., 1.)

my_vbo = vbo.VBO(
        array( [
            ( quad[0], quad[1], 0, tex[0], tex[1]),
            ( quad[0], quad[3], 0, tex[0], tex[3]),
            ( quad[2], quad[3], 0, tex[2], tex[3]),
            ( quad[2], quad[1], 0, tex[2], tex[1])
        ],'f,f,f,f,f')
    )

# texture
bitmap, palette = checkboard()
height, width = bitmap.shape
f_image = (array(bitmap, 'f') + .4) / 256.0

# Image to be displayed
image_id = glGenTextures(1)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, image_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
             GL_ALPHA, GL_FLOAT, f_image)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glActiveTexture(GL_TEXTURE0)

# palette
f_palette = (palette / float32(255))

palette_id = glGenTextures(1)
glEnable(GL_TEXTURE_1D)
glBindTexture(GL_TEXTURE_1D, palette_id)
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0,
             GL_RGBA, GL_FLOAT, f_palette)
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
#glActiveTexture(GL_TEXTURE1)

# shaders
VERTEX_SHADER = shaders.compileShader("""#version 330

layout(location = 0) in vec4 position;
uniform vec2 offset ;

void main()
{
      gl_FrontColor = gl_Color;
      gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;
      gl_Position = vec4((offset.x + position.x - 256) / 256, (256 - offset.y - position.y)/256, 0.0, 1.0);
}""", GL_VERTEX_SHADER)

FRAGMENT_SHADER = shaders.compileShader("""#version 330
    uniform sampler2D texture; 
    uniform sampler1D palette;

    void main() 
    { 
        vec2 uv = gl_TexCoord[0].xy; 
        vec4 color = texture2D(texture, uv); 
        gl_FragColor = texture1D(palette, color.a) ;
    }""", GL_FRAGMENT_SHADER)

shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)

# uniform variables
offset_uniform_loc = glGetUniformLocation(shader, "offset")
texture_uniform_loc = glGetUniformLocation(shader, 'texture' )
palette_uniform_loc = glGetUniformLocation(shader, 'palette' )


def display(w, h):
    """Render the geometry for the scene."""
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0, w, 0, h, -1, 1)

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity()

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glEnable( GL_TEXTURE_2D )
    glActiveTexture( GL_TEXTURE0 )
    glBindTexture( GL_TEXTURE_2D, image_id)

    glEnable( GL_TEXTURE_1D )
    glActiveTexture( GL_TEXTURE1 )

    shaders.glUseProgram(shader)
    shaders.glUniform1i(texture_uniform_loc, 0)
    shaders.glUniform1i(palette_uniform_loc, 1)
    shaders.glUniform2f(offset_uniform_loc, 0, 0)

    try:
        my_vbo.bind()
        try:
            glEnableClientState(GL_VERTEX_ARRAY)
            glEnableClientState(GL_TEXTURE_COORD_ARRAY)
            glVertexPointer(3, GL_FLOAT, 20, my_vbo)
            glTexCoordPointer(2, GL_FLOAT, 20, my_vbo + 12)

            glBindTexture( GL_TEXTURE_1D, palette_id)
            glDrawArrays(GL_QUADS, 0, 4)
        finally:
            my_vbo.unbind()
            glDisableClientState(GL_TEXTURE_COORD_ARRAY)
            glDisableClientState(GL_VERTEX_ARRAY)
    finally:
        shaders.glUseProgram( 0 )

    glutSwapBuffers()


glutReshapeFunc(reshape)
glutIdleFunc(glutPostRedisplay)
glutMainLoop()

推荐答案

使用 GL_CLAMP_TO_EDGE 作为纹理的 GL_TEXTURE_WRAP_S.

出现此问题的部分原因是纹理坐标 1.0 引用了超出最后一个纹素中心的位置.夹到边缘将使您的坐标被夹在 S 方向上的边缘纹素的中心.

Use GL_CLAMP_TO_EDGE for your texture's GL_TEXTURE_WRAP_S.

This problem is occurring partly because the texture coordinate 1.0 references a location that is beyond the last texel's center. Clamp to edge will make it so that your coordinates are clamped to the centers of the edge texels in the S direction.

当纹理被采样时,如果一个坐标不是指一个确切的纹素中心,那么过滤模式和包裹行为将决定从哪里获取纹素.默认情况下,OpenGL 使用重复模式,因此纹理坐标接近 1.0 的最近邻居(最近的纹素中心)可能来自纹理的另一侧(重复).对于普通图像,您甚至可能不会注意到这种行为,但是当您绕到查找表的另一侧时,这种不连续性可能非常明显.

When textures are sampled, if a coordinate does not refer to an exact texel center then the filter mode and wrap behavior will dictate where the texel(s) are fetched from. By default OpenGL uses a repeat mode, so the nearest neighbor (closest texel center) to a texture coordinate close to 1.0 may come from the other side of your texture (repeat). With ordinary images you might not even notice this behavior, but when you wrap around to the other side of a lookup table the discontinuity can be very obvious.

http://i.msdn.microsoft.com/dynimg/IC83860.gif

注意纹理坐标1.0实际上是如何引用调色板条目30之间的边界的?

Notice how texture coordinate 1.0 actually refers to the boundary between palette entry 3 and 0?

简短的版本是,如果您的范围是从 0.01.0,那么您的纹理坐标都没有引用纹素中心,您很容易最终采样错误的纹素.您需要调整坐标,以便不为每个调色板条目使用纹素边界.<小时>或者,由于您使用的是 GLSL ≥130 您可以使用 texelFetch (...) 并完全跳过所有这些标准化纹理坐标废话.如果您想使用其 a 组件获取纹理的调色板条目,请尝试以下操作:

The short version is if your range is from 0.0 to 1.0 then none of your texture coordinates are referencing texel centers and you can easily wind up sampling the wrong texel. You need to adjust your coordinates so that you are not using the texel boundary for each palette entry.


Alternatively, since you are using GLSL ≥ 130 you can use texelFetch (...) and skip all of this normalized texture coordinate nonsense altogether. If you want to get the palette entry for your texture using its a component, then try something like this:

gl_FragColor = texelFetch (palette, (int)(texture2D (texture, uv).a * 255.0), 0);

这将显式获取整数索引给出的纹素.您不必担心最近的邻居、包裹模式、过滤等.

This will explicitly fetch the texel given by the integer index. You do not have to worry about nearest neighbor(s), wrap modes, filtering, etc.

这篇关于接近 1 的纹理坐标表现得很奇怪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆