英文:
glReadPixels does not write data to array
问题
我被卡在glReadPixels
上了:似乎没有数据写入输出数组。
根据手册(https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glReadPixels.xml)中的说明,如果发生错误,则不会更改数据的内容,但是glGetError()
没有返回任何错误。
我真的不明白出了什么问题...这是一个在屏幕上采样随机点的代码示例(使用golang)。它始终打印[0 0 0]
,尽管没有像素应该是黑色的(甚至不是背景)。
package main
import (
"fmt"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/veandco/go-sdl2/sdl"
"math/rand"
"os"
"runtime"
)
func main() {
// 随机崩溃,这似乎可以修复
runtime.LockOSThread()
var wWidth, wHeight int = 500, 500
// 初始化SDL
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
panic(err)
}
defer sdl.Quit()
// 初始化OpenGL
if err := gl.Init(); err != nil {
panic(err)
}
// 创建支持OpenGL的窗口
win, err := sdl.CreateWindow(
"glReadPixels Test",
sdl.WINDOWPOS_CENTERED,
sdl.WINDOWPOS_CENTERED,
wWidth, wHeight,
sdl.WINDOW_OPENGL)
if err != nil {
panic(err)
}
defer win.Destroy()
// 设置OpenGL上下文
var ctx sdl.GLContext
ctx, err = sdl.GL_CreateContext(win)
if err != nil {
panic(err)
}
defer sdl.GL_DeleteContext(ctx)
// 设置
initGL(wWidth, wHeight)
for {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch event.(type) {
case *sdl.QuitEvent:
os.Exit(0)
}
}
// 清除并绘制
gl.Clear(gl.COLOR_BUFFER_BIT)
gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
rx, ry := rand.Intn(wWidth), rand.Intn(wHeight)
gl.ReadBuffer(gl.FRONT) // 使用gl.BACK也不起作用
data := make([]byte, 3)
//data[0], data[1], data[2] = 123, 213, 132 // 测试是否被覆盖
gl.ReadPixels(int32(rx), int32(ry), 1, 1, gl.RGB, gl.UNSIGNED_BYTE, gl.Ptr(&data))
fmt.Println("Read at", rx, ry, data)
// 在窗口上显示
sdl.GL_SwapWindow(win)
// 检查错误...
if gl.GetError() != gl.NO_ERROR {
fmt.Println("GL ERROR Somewhere!")
}
sdl.Delay(uint32(300))
}
}
func initGL(winW, winH int) {
gl.ClearColor(0.2, 0.2, 0.3, 1.0)
gl.Viewport(0, 0, int32(winW), int32(winH))
// 着色器源代码
vertShaderSrc := gl.Str(`
#version 130
in vec3 vertPos;
in vec2 vertUV;
out vec2 fragUV;
void main() {
gl_Position = vec4(vertPos, 1.0f);
fragUV = vertUV;
}
` + "\x00")
fragShaderSrc := gl.Str(`#version 130
in vec2 fragUV;
out vec3 outColor;
void main() {
if (fragUV.x < 0.5 ^^ fragUV.y < 0.5)
outColor = vec3(1.0, 0.0, 0.0);
else
outColor = vec3(0.0, 0.0, 1.0);
}
` + "\x00")
// 着色器
var vShader, fShader uint32
vShader = gl.CreateShader(gl.VERTEX_SHADER)
fShader = gl.CreateShader(gl.FRAGMENT_SHADER)
gl.ShaderSource(vShader, 1, &vertShaderSrc, nil)
gl.ShaderSource(fShader, 1, &fragShaderSrc, nil)
gl.CompileShader(vShader)
gl.CompileShader(fShader)
// 程序
var progr uint32 = gl.CreateProgram()
gl.AttachShader(progr, vShader)
gl.AttachShader(progr, fShader)
gl.BindAttribLocation(progr, 0, gl.Str("vertPos\x00"))
gl.BindAttribLocation(progr, 1, gl.Str("vertUV\x00"))
gl.LinkProgram(progr)
gl.UseProgram(progr)
const N float32 = -0.5
const P float32 = 0.5
// 设置“画布”:一个简单的四边形
canvasData := []float32{
N, N, 0, 0.0, 0.0,
P, N, 0, 1.0, 0.0,
N, P, 0, 0.0, 1.0,
P, P, 0, 1.0, 1.0,
}
var vboID uint32
gl.GenBuffers(1, &vboID)
gl.BindBuffer(gl.ARRAY_BUFFER, vboID)
gl.BufferData(gl.ARRAY_BUFFER, len(canvasData)*4, gl.Ptr(canvasData), gl.STATIC_DRAW)
var vaoID uint32
gl.GenVertexArrays(1, &vaoID)
gl.BindVertexArray(vaoID)
gl.EnableVertexAttribArray(0)
gl.EnableVertexAttribArray(1)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0))
gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4))
}
希望对你有所帮助!
英文:
I am stuck with glReadPixels
: it appears that no data is written in the output array.
From the manual (https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glReadPixels.xml) it's written that If an error is generated, no change is made to the contents of data., but apparently no error is returned by glGetError()
.
I really can't understand what's wrong... Here's the code - in golang - that samples random point in the screen. It always print [0 0 0]
, although no pixel should be black (not even the background).
package main
import (
"fmt"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/veandco/go-sdl2/sdl"
"math/rand"
"os"
"runtime"
)
func main() {
// Random crashes, this seems to fix
runtime.LockOSThread()
var wWidth, wHeight int = 500, 500
// Initialize SDL
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
panic(err)
}
defer sdl.Quit()
// Initialize OpenGL
if err := gl.Init(); err != nil {
panic(err)
}
// Create the OpenGL-capable window
win, err := sdl.CreateWindow(
"glReadPixels Test",
sdl.WINDOWPOS_CENTERED,
sdl.WINDOWPOS_CENTERED,
wWidth, wHeight,
sdl.WINDOW_OPENGL)
if err != nil {
panic(err)
}
defer win.Destroy()
// Setup OpenGL context
var ctx sdl.GLContext
ctx, err = sdl.GL_CreateContext(win)
if err != nil {
panic(err)
}
defer sdl.GL_DeleteContext(ctx)
// Settings
initGL(wWidth, wHeight)
for {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch event.(type) {
case *sdl.QuitEvent:
os.Exit(0)
}
}
// Clear and draw
gl.Clear(gl.COLOR_BUFFER_BIT)
gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
rx, ry := rand.Intn(wWidth), rand.Intn(wHeight)
gl.ReadBuffer(gl.FRONT) // Not working with gl.BACK either
data := make([]byte, 3)
//data[0], data[1], data[2] = 123, 213, 132 // Test if it's overwritten
gl.ReadPixels(int32(rx), int32(ry), 1, 1, gl.RGB, gl.UNSIGNED_BYTE, gl.Ptr(&data))
fmt.Println("Read at", rx, ry, data)
// Show on window
sdl.GL_SwapWindow(win)
// Check for errors...
if gl.GetError() != gl.NO_ERROR {
fmt.Println("GL ERROR Somewhere!")
}
sdl.Delay(uint32(300))
}
}
func initGL(winW, winH int) {
gl.ClearColor(0.2, 0.2, 0.3, 1.0)
gl.Viewport(0, 0, int32(winW), int32(winH))
// Shader sources
vertShaderSrc := gl.Str(`
#version 130
in vec3 vertPos;
in vec2 vertUV;
out vec2 fragUV;
void main() {
gl_Position = vec4(vertPos, 1.0f);
fragUV = vertUV;
}
` + "\x00")
fragShaderSrc := gl.Str(`#version 130
in vec2 fragUV;
out vec3 outColor;
void main() {
if (fragUV.x < 0.5 ^^ fragUV.y < 0.5)
outColor = vec3(1.0, 0.0, 0.0);
else
outColor = vec3(0.0, 0.0, 1.0);
}
` + "\x00")
// Shaders
var vShader, fShader uint32
vShader = gl.CreateShader(gl.VERTEX_SHADER)
fShader = gl.CreateShader(gl.FRAGMENT_SHADER)
gl.ShaderSource(vShader, 1, &vertShaderSrc, nil)
gl.ShaderSource(fShader, 1, &fragShaderSrc, nil)
gl.CompileShader(vShader)
gl.CompileShader(fShader)
// Program
var progr uint32 = gl.CreateProgram()
gl.AttachShader(progr, vShader)
gl.AttachShader(progr, fShader)
gl.BindAttribLocation(progr, 0, gl.Str("vertPos\x00"))
gl.BindAttribLocation(progr, 1, gl.Str("vertUV\x00"))
gl.LinkProgram(progr)
gl.UseProgram(progr)
const N float32 = -0.5
const P float32 = 0.5
// Setup the "canvas": a simple quad
canvasData := []float32{
N, N, 0, 0.0, 0.0,
P, N, 0, 1.0, 0.0,
N, P, 0, 0.0, 1.0,
P, P, 0, 1.0, 1.0,
}
var vboID uint32
gl.GenBuffers(1, &vboID)
gl.BindBuffer(gl.ARRAY_BUFFER, vboID)
gl.BufferData(gl.ARRAY_BUFFER, len(canvasData)*4, gl.Ptr(canvasData), gl.STATIC_DRAW)
var vaoID uint32
gl.GenVertexArrays(1, &vaoID)
gl.BindVertexArray(vaoID)
gl.EnableVertexAttribArray(0)
gl.EnableVertexAttribArray(1)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0))
gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4))
}
答案1
得分: 2
问题可能出在这里:
gl.ReadPixels(int32(rx), int32(ry), 1, 1, gl.RGB, gl.UNSIGNED_BYTE, gl.Ptr(&data))
具体来说是这一部分:
gl.Ptr(&data)
该函数的文档如下:
// Ptr接受一个切片或指针(指向一个标量值或数组/切片的第一个元素)并返回其GL兼容的地址。
你传递了一个指向切片的指针。如果你看一下实现,这是有问题的。
func Ptr(data interface{}) unsafe.Pointer {
if data == nil {
return unsafe.Pointer(nil)
}
var addr unsafe.Pointer
v := reflect.ValueOf(data)
switch v.Type().Kind() {
case reflect.Ptr:
e := v.Elem()
switch e.Kind() {
case
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
addr = unsafe.Pointer(e.UnsafeAddr())
}
case reflect.Uintptr:
addr = unsafe.Pointer(v.Pointer())
case reflect.Slice:
addr = unsafe.Pointer(v.Index(0).UnsafeAddr())
default:
panic(fmt.Sprintf("Unsupported type %s; must be a pointer, slice, or array", v.Type()))
}
return addr
}
如你所见,如果你传递的是指向除了基本的uint/float/int之外的任何类型的指针,它将会静默失败,并返回一个空的unsafe.Pointer
,如果它不是正确的类型。所以你要么传递gl.Ptr(&data[0])
,要么传递gl.Ptr(data)
。说实话,这样不会导致段错误,有点令人惊讶。
英文:
The problem is likely
gl.ReadPixels(int32(rx), int32(ry), 1, 1, gl.RGB, gl.UNSIGNED_BYTE, gl.Ptr(&data))
Specifically
gl.Ptr(&data)
The documentation for that function is:
// Ptr takes a slice or pointer (to a singular scalar value or the first
// element of an array or slice) and returns its GL-compatible address.
You're passing a POINTER to a slice. This is... problematic if you look at the implementation.
func Ptr(data interface{}) unsafe.Pointer {
if data == nil {
return unsafe.Pointer(nil)
}
var addr unsafe.Pointer
v := reflect.ValueOf(data)
switch v.Type().Kind() {
case reflect.Ptr:
e := v.Elem()
switch e.Kind() {
case
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
addr = unsafe.Pointer(e.UnsafeAddr())
}
case reflect.Uintptr:
addr = unsafe.Pointer(v.Pointer())
case reflect.Slice:
addr = unsafe.Pointer(v.Index(0).UnsafeAddr())
default:
panic(fmt.Sprintf("Unsupported type %s; must be a pointer, slice, or array", v.Type()))
}
return addr
}
As you can see, if you pass in a pointer to anything other than a basic uint/float/int, it will fail silently and return a null unsafe.Pointer
if it's not of the correct type. So you either want to pass in gl.Ptr(&data[0])
or gl.Ptr(data)
. It's honestly kind of surprising this wasn't segfaulting.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论