Render to 3D Texture with glFrameBufferTexture(..)

Okay, so here’s my problem. I’m trying to render points (GL_POINTS) to a 3D texture but get an INVALID_OPERATION on either of glDrawArraysInstanced or glDrawArrays. Here’s what’s happening:

  1. create a vbo of a list of points and two extra floats for texture coordinates that i use in my shader. that’s 5 floats per point, 3 for position, 2 for texture coordinates
  2. create a 256x256x256 3d texture of internal format GL_RGBA8 to render to
    3)set viewport to the width and height of my 3d texture (each layer’s width and height). 256x256
  3. on the frame buffer object I already have created I call glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 3Dtexture, 0 ) to bind all layers of the texture. Also, I enable that color attachment as the only draw buffer using glDrawBuffers(…)
  4. call glDrawArrays or glDrawArraysInstanced with my vbo bound using GL_POINTS. get an invalid operation from opengl. (if I replace this step with glClear, the clear call works and clears my texture)
  5. unbind the frame buffer

I am using a geometry program that outputs gl_Layer so that each fragment knows where it’s going in the 3D texture. The program compiles and links correctly. The program is bound before I call glDrawArrays / glDrawArraysInstanced. The frame buffer is also frame buffer complete.

I really need to be able to output gl_Layer in my geometry program and I just can’t figure out why this is not working.

I’ve checked to see if my VBO is proper and it seems to be. I’ve uploaded contiguous data where every 20 bytes defines a point to render with the first 12 bytes the position and the next 8 the texture coordinates. Also, setting glVertexAttribPointer is called correctly for both the positions and texture coordinates (correct start offset and stride). Also, both vertex attribute arrays are enabled.

I really don’t know what’s going on. Does the overview of the process make sense? Is there something I’m missing there?

This is all done using opengl 3.2 with latest nvidia drivers

Here is a code listing of GL calls


vBuff = glGenBuffers(1);
float vertexData[] = { 0.55, 0.24, 0.123, 0.5, 0.5,
                 0.57, 0.24, 0.123, 0.51, 0.5,
                    ...... // 400 of these
                 };

glBindBuffer(GL_ARRAY_BUFFER, vBuff);
glBufferData( GL_ARRAY_BUFFER, 8000, vertexData, GL_DYNAMIC_DRAW);
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 20, 0);
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 20, 12);
glEnableVertexAttribArray( 1 );

texture3D = glGenTextures(1);
glBindTexture( GL_TEXTURE_3D, texture3D );
glTexImage3D( GL_TEXTURE_3D, 0, GL_RGBA8, 256, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

// framebufferId I have, I'll skip creation code cause I know it's 100% correct
// It's bound

glBindFramebuffer( GL_FRAMEBUFFER, framebufferId );

glFrameBufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0 );

drawBuffers[] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers( drawBuffers );

glBindTexture( GL_TEXTURE_3D, 0);

glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE );

glViewport( 0, 0, 256, 256 );

// Setup shader program uniforms
// This involves binding some textures setting their uniforms, and setting other uniforms 

glDrawArrays( GL_POINTS, 0, 400 );
// or
// glDrawArraysInstanced( GL_POINTS, 0, 400, 2 );
// both give invalid operation



Did you check to see if the framebuffer is complete? If you have a depth buffer, and you want to do layered rendering, the depth buffer must also be a layered image.

Thanks, but yes I check if the framebuffer is complete and it is. I also do not use a depth buffer.

I actually just solved most of my problem 10 seconds ago.

My geometry shader in the out layout block was not specifying the max_vertices

I had: layout(points) out;
But needed: layout(points, max_vertices=32) out;

Slight oversight.

HOWEVER, now I get an ‘invalid value’ on


glBindFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0)

when trying to unbind the texture. The specs say that binding 0 should just unbind (like all other bind calls in GL).

Any ideas there?

I believe the correct way to unbind something from an attachment is to call glFramebufferRenderbuffer with a renderbuffer name of 0. The spec specifically says that passing 0 is acceptable to that function and will cause unattachment, while the spec does not say this for glFramebufferTexture (of any kind).

Yes it does:


  void FramebufferTextureEXT(enum target, enum attachment,
                                 uint texture, int level);

... If <texture> is zero, any image or array of images attached to the
    attachment point named by <attachment> is detached, and the state of the
    attachment point is reset to its initial values.  <level> is ignored if
    <texture> is zero.

Exactly, so what else could go wrong here?

Now that you’ve discovered a simple yet significant mistake in your shader code there’s now the specter of similar miscues darkening the door to enlightenment IMHO.

I doubt a shader has anything to do with unbinding a texture from a framebuffer but okay


Vertex shader:
----------------------
#version 150 core

in vec3 in_Position;
in vec2 in_MaskTextureCoordinates;

out vec2 ex_MaskTextureCoordinates;
out vec3 ex_DepthPropogatedPosition;

uniform vec3 viewDirection;

void main( void )
{
    gl_Position = vec4( in_Position, 1.0 );
    ex_MaskTextureCoordinates = in_MaskTextureCoordinates;
    ex_DepthPropogatedPosition = in_Position + viewDirection * float( gl_InstanceID );
}
--------------------

Geometry shader

------------------

#version 150 core

#define EPSILON 0.0001

layout(points) in;
layout(points, max_vertices = 32) out;

in vec2 ex_MaskTextureCoordinates[1];
in vec3 ex_DepthPropogatedPosition[1];

flat out float gaussianWeight1D;
flat out float maskValue;

uniform sampler2D maskTexture;
uniform sampler1D gaussianTexture1D;
uniform vec3 volumeDimensions;

const int radiationVolumeWidth = 6;
const int halfRadiationVolumeWidth = radiationVolumeWidth / 2;
const float radiationVolumeWidthFloat = float( radiationVolumeWidth );

void main( void )
{
    float sampledMaskValue = texture( maskTexture, ex_MaskTextureCoordinates[0] ).a;
    
    // There's basically no mask here so why are trying to do anything?
    if( sampledMaskValue < EPSILON )
    {
        return;
    }
    
    // ex_DepthPropogatedPosition.x and ex_DepthPropogatedPosition.y are on the interval [0,1]
    // We want them to be on the interval [-1, 1]
    vec2 particlePosition = ( 2.0 * ex_DepthPropogatedPosition[0].xy ) - 1.0;
    vec4 screenCoordinates = vec4( ex_DepthPropogatedPosition[0].xy, 0.0, 1.0 );
    
    // particleZ is on the interval [0,1]. Use it to calculate where we are doing the radiation.
    float particleZ = ex_DepthPropogatedPosition[0].z;
    
    float radiationCenterLayer = particleZ * volumeDimensions.z;
    int firstLayer = int( round( radiationCenterLayer ) ) - halfRadiationVolumeWidth;
    int lastLayer = firstLayer + radiationVolumeWidth;
    
    // The radiation center is actually in the center of a pixel, not the right edge, so subtract 0.5
    radiationCenterLayer -= 0.5;
    
    // The sampling position in the 1d gaussian texture. To be used in the loop.
    float currentSamplingPosition;
    
    // Do the actual point radiation.
    // A point sprite is emitted for each layer in the radiation volume
    for( int i = firstLayer; i < lastLayer; ++i )
    {
        // Basically clamp the radiation volume to inside the volume.
        if( i >= 0 && i < volumeDimensions.z )
        {
            // Gaussian is centered in the texture at 0.5.
            currentSamplingPosition =  0.5 + ( ( float( i ) - radiationCenterLayer ) / radiationVolumeWidth );
            
            // All the outputs for each vertex
            gaussianWeight1D = texture( gaussianTexture1D, currentSamplingPosition ).r;
            maskValue = sampledMaskValue;
            gl_Position = screenCoordinates;
            gl_Layer = i;
            gl_PointSize = radiationVolumeWidthFloat;
            EmitVertex();
        }
    }
}

------

Fragment shader

------

#version 150 core

flat in float gaussianWeight1D;
flat in float maskValue;

out vec4 out_Color;

uniform sampler2D gaussianTexture2D;
uniform float gaussianCutoff;

void main( void )
{
    // Since all points are point sprites they all output texture coordinates to gl_PointCoord after going 
    // through the pipeline
    float gaussianWeight2D = texture( gaussianTexture2D, gl_PointCoord ).r;
    
    float totalGaussian = gaussianWeight2D * gaussianWeight1D;
    
    if( totalGaussian < gaussianCutoff )
    {
        discard;
    }
    
    out_Color = vec4( maskValue * 0.1, 0.0, 0.0, totalGaussian );
    
}

All compile and link successfully but as I’ve learned from the layout block that doesn’t mean jack (since layout can be set programatically also).

SOLVED: First my geometry shader problem plus not having to unbind the layered texture. It will be unbound once you try to bind any other texture.