1附近的纹理坐标表现异常 [英] Texture coordinates near 1 behave oddly

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

问题描述

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

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 [
  • color 0 lies in interval [0 ; 1/256[
  • color 1 lies in interval [1/256 ; 2/256[ ...
  • color 255 lies in interval [255/256 ; 1[

我使用公式x_float =(x_int + .4)/256将自己的整数索引转换为0到1之间的浮点数,以确保发生了什么,即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的两倍,并且从此开始,所有内容都移动了一个:最后一个颜色是0xFE为0xFF.

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 实际上是如何指代调色板条目 3 0 之间的边界的?

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

简短的版本是,如果您的范围是从 0.0 1.0 ,则您的纹理坐标都没有参考texel中心,您可以轻松结束对错误texel的采样.您需要调整坐标,以免每个调色板条目都使用纹理像素边界.


另外,由于您使用的是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天全站免登陆