2
$\begingroup$

I am trying to pass image texture to fragment shader and read it back. But it only works if the color of the pixel is set to 0.0 or 1.0. The value returns to the buffer if the value is set to, for example, 0.2, is incorrect. I am using blender 2.8 with python script. I am totally new to Blender, so any suggestions are appreciated. Here is my code so far:

import bpy
import gpu
import bgl
import random
from mathutils import Matrix
from gpu_extras.batch import batch_for_shader
import numpy as np
from math import *
# from gpu_extras.presets import draw_circle_2d
import time

IMAGE_NAME = "Generated Image"
WIDTH = 4
HEIGHT = 4

vertex_shader = '''
    in vec2 position;
    in vec2 uv;

    out vec2 uvInterp;

    void main()
    {
        uvInterp = uv;
        gl_Position = vec4(position, 0.0, 1.0);
    }
'''

fragment_shader = '''
    uniform sampler2D image;

    out vec4 color;

    in vec2 uvInterp;

    void main()
    {
        vec4 result = texture(image, uvInterp);
        color = result;
    }
'''
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
batch = batch_for_shader(
    shader, 'TRI_FAN',
    {
        "position": ((-1, -1), (1, -1), (1, 1), (-1, 1)),
        "uv": ((0, 0), (1, 0), (1, 1), (0, 1)),
    },
)

# print(buffer)
# push buffer to blender image
if IMAGE_NAME in bpy.data.images:
    image = bpy.data.images[IMAGE_NAME]
    if image:
        bpy.data.images.remove(image)
        bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT)
else:
    bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT)

image = bpy.data.images[IMAGE_NAME]

#FIXED: set data
for i in range(0, WIDTH*HEIGHT*4, 4):
    image.pixels[i] = 0.2
    image.pixels[i+1] = 0.3
    image.pixels[i+2] = 0.1
    image.pixels[i+3] = 0.4

print(list(image.pixels))
# load to gpu
if image.gl_load():
    raise Exception()


offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)
# at the beginning:
start_time = time.time()
with offscreen.bind():
    bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
    bgl.glActiveTexture(bgl.GL_TEXTURE0)
    bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)

    shader.bind()
    shader.uniform_int("image", 0)
    batch.draw(shader)
    buffer = bgl.Buffer(bgl.GL_FLOAT, WIDTH * HEIGHT * 4)
    bgl.glReadBuffer(bgl.GL_BACK)
    bgl.glReadPixels(0, 0, WIDTH, HEIGHT, bgl.GL_RGBA, bgl.GL_FLOAT, buffer)

    # at the end of the program:
    print("glReadPixels: %f seconds" % (time.time() - start_time))
offscreen.free()
print(buffer)
$\endgroup$
2
$\begingroup$

Somehow, I figured out myself, this is quite a newbie mistake. The generated image should be created with float_buffer.

from:

bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT)

to:

bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT, alpha=True, float_buffer=True)

One more thing is that the alpha value should be 1.0 since it will be used as masks for other. Ref from documentation

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.