Я изучаю OpenGL, собирая различные примеры. Моя цель — иметь приложение, которое отображает 2D-изображение, пиксель за пикселем (т. е. без геометрических преобразований), позволяя мне манипулировать значениями пикселей внутри фрагментного шейдера. Я думаю, что приближаюсь к этой цели, но мои текстуры становятся пространственно искаженными, и я не понимаю, почему.
Мой полный код ниже. Это работает так, как я ожидаю, если я использую строку с пометкой OPTION A в своем фрагментном шейдере: это дает мне возможность манипулировать значениями пикселей в координатах изображения. Проблема возникает, когда я раскомментирую OPTION B, который пытается получить значения цвета фрагмента через texture2D из существующей текстуры, объявленной как uniform sampler2D.
Проблема, похоже, коренится в том, как я создаю текстуру. Мой источник изображения — это число с плавающей запятой numpy.array с 3 слоями (RGB). Я пробую самый простой тестовый образец, который только могу придумать: черная текстура, первая половина пикселей которой стала красной (см. строку с комментарием TEST). Пиксели становятся красными, поэтому я, по-видимому, правильно обращаюсь к слоям. Но «первая половина» пикселей отображается не как связный блок строк или столбцов (и то, и другое я мог ожидать), а скорее по следующему очень причудливому шаблону:
Помимо моей основной проблемы с пиксельной компоновкой, комментарии в стиле проверки кода также очень приветствуются, потому что я уверен, что есть вещи, которые мне не нужно и/или не следует делать. Пожалуйста, простите устаревший код GLSL/подход OpenGL, который кажется необходимым для поддержки моего MacBook 2013 года, работающего под управлением El Capitan (GLSL v. 1.20), в дополнение к моим более современным машинам с поддержкой OpenGL, работающим под управлением Windows 10 (GLSL v. 4.x) .
# Display framework adapted from:
# - Pygame/PyopenGL examples by Bastiaan Zapf, Apr 2009 ( http://python-opengl-examples.blogspot.com/ )
#
# Shader code for passing texture coordinates from vertex to fragment shader and reading from a uniform sampler2D, adapted from:
# - "Modern OpenGL Textures" tutorial by Tom Dalling at http://www.tomdalling.com/blog/modern-opengl/02-textures/ (but note that we're really trying to support legacy OpenGL, not "modern")
#
# (Failing?) attempt to make texture data accessible to the shader in the first place, adapted from:
# - SO question by GergelyH at http://stackoverflow.com/questions/43482690
import sys
import time
import random
from math import * # trigonometry
import numpy
import pygame, pygame.locals # just to get a display
import OpenGL
from OpenGL.GL import *
from OpenGL.GLU import *
canvasWidth, canvasHeight = 800,600
# get an OpenGL surface
pygame.init()
pygame.display.set_mode( ( canvasWidth, canvasHeight ), pygame.OPENGL | pygame.DOUBLEBUF )
glEnable(GL_DEPTH_TEST)
GLSL_Version = glGetString(GL_SHADING_LANGUAGE_VERSION)
print( 'GLSL Version = ' + GLSL_Version )
print( 'PyOpenGL Version = ' + OpenGL.__version__ )
def Shader(type, source):
shader = glCreateShader(type)
glShaderSource(shader, source)
glCompileShader(shader)
result = glGetShaderiv(shader, GL_COMPILE_STATUS)
if result != 1: raise Exception( "Shader compilation failed:\n" + glGetShaderInfoLog(shader))
return shader
vertex_shader = Shader(GL_VERTEX_SHADER, """\
varying vec2 fragTextureCoordinate;
void main(void)
{
fragTextureCoordinate = gl_Vertex.xy;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; // TODO: is there a way of shortcutting this matrix multiplication...?
}
""");
fragment_shader = Shader(GL_FRAGMENT_SHADER, """\
uniform sampler2D tex;
varying vec2 fragTextureCoordinate;
void main(void)
{
gl_FragColor = vec4(
fragTextureCoordinate.x > %f, // right half red/yellow
fragTextureCoordinate.y > %f, // top half green/yellow
abs( fragTextureCoordinate.x - fragTextureCoordinate.y ) < 0.5, // diagonal blue/cyan/white line
1 // opaque
); // OPTION A: pixel-addressing sanity check. This behaves as expected. I can address the image in image coordinates, although note that (0,0) is bottom left
gl_FragColor = texture2D( tex, fragTextureCoordinate );
// OPTION B: texture mapping attempt. The channels seem to be arranged sanely, but the spatial ordering of the pixels is really strange
}
""" % ( canvasWidth / 2.0, canvasHeight / 2.0 ) );
# build shader program
program = glCreateProgram()
glAttachShader( program, vertex_shader )
glAttachShader( program, fragment_shader )
glLinkProgram( program )
# try to activate/enable shader program, handling errors wisely
try:
glUseProgram(program)
except OpenGL.error.GLError:
print( glGetProgramInfoLog( program ) )
raise
gluOrtho2D(0, canvasWidth, 0, canvasHeight)
glClearColor(0.0, 0.0, 0.0, 1.0)
import numpy;
#img = numpy.zeros( [ canvasHeight, canvasWidth, 3 ], 'float32' ); ; img[ :, canvasWidth//2:, 0 ] = 0; img[ canvasHeight//2:, :, 1 ] = 0; img[ :, :, 2 ] = 0
img = numpy.zeros( [ canvasHeight * canvasWidth * 3 ], 'float32' ) # dtype='float32' here corresponds to type=GL_FLOAT and internalFormat=GL_RGB32F below
img[ : img.size//2 : 3 ] = 1.0 # TEST: light up every third pixel (i.e. red channel only) up until halfway through the image
######
# texture-loading section adapted from question by GergelyH at http://stackoverflow.com/questions/43482690
glActiveTexture( GL_TEXTURE0 )
textureID = glGenTextures( 1 )
glBindTexture( GL_TEXTURE_2D, textureID )
glEnable( GL_TEXTURE_2D )
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ) # no apparent effect
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB32F, canvasWidth, canvasHeight, 0, GL_RGB, GL_FLOAT, img.tostring() )
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST )
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST )
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ) # no apparent effect
try: glGenerateMipmap( GL_TEXTURE_2D ) # NB: seems to be available in PyOpenGL 3.0.2 but not 3.0.1; has no apparent effect
except NameError, err: print( err )
######
glUniform1i( glGetUniformLocation( program, "tex" ), 0 ) # set to 0 because the texture will be bound to GL_TEXTURE0, says Tom Dalling (that means it looks like we could only ever do this with 32 textures max)
# create display list
glNewList( 1, GL_COMPILE )
glBegin( GL_QUADS )
glColor3f( 1, 1, 1 )
glNormal3f( 0, 0, 1 )
glVertex3f( 0, canvasHeight, 0 )
glVertex3f( canvasWidth, canvasHeight, 0 )
glVertex3f( canvasWidth, 0, 0 )
glVertex3f( 0, 0, 0 )
# NB: unlike GergelyH's answer to his own question, it seems to make no difference
# whether we call glTexCoord here---presumably because we're trying to do all the
# texture mapping in the shader itself
glEnd()
glEndList()
done = False
pygame.event.get() # flush the event queue
while not done:
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
glCallList( 1 )
#print( Time() - t )
pygame.display.flip()
time.sleep( 0.014 ) # TODO: hardcoded
# Press 'q' to quit or 's' to save a timestamped snapshot
for event in pygame.event.get():
if event.type == pygame.locals.QUIT: done = True
elif event.type == pygame.locals.KEYUP and event.key in [ ord( 'q' ), 27 ]: done = True
elif event.type == pygame.locals.KEYUP and event.key in [ ord( 's' ) ]:
try: from PIL import Image
except ImportError: import Image
filename = time.strftime('snapshot-%Y%m%d-%H%M%S.png')
rawRGB = glReadPixels( 0, 0, canvasWidth, canvasHeight, GL_RGB, GL_UNSIGNED_BYTE )
Image.frombuffer( 'RGB', [ canvasWidth, canvasHeight ], rawRGB, 'raw', 'RGB', 0, 1 ).transpose( Image.FLIP_TOP_BOTTOM ).save( filename )
pygame.display.quit()
inиout. - person jez   schedule 14.06.2017img.tostring()в glTexImage2D... что возвращаетtostring?! Что касается вашего подвопроса относительно умножения матрицы сокращения, да, просто определите свои вершины в диапазоне NDC (-1, + 1), при этом, поскольку вы используете здесь старую школу GL, вы можете просто отбросить материал вершинного шейдера и списка вершин и просто используйтеglRects, чтобы сделать свой розыгрыш. - person LJᛃ   schedule 15.06.2017