Getting uv coordinates in GL3 fragment shader wrapped around ofDrawRectangle

#1

If I wrap an ofShader around a call to ofDrawRectangle(), is there a way for me to get the uv coordinates within the fragment shader, such as vec2(0, 0) being the top left of my rectangle, and vec2(1, 1) being the bottom right?

I tried to pass the rectangle’s width and height as uniforms, and divide gl_FragCoord / vec2(uniformWidth, uniformHeight) - but it seems gl_FragCoord relates to the full screen size, not relative to my drawn rectangle.

Similarly, in the openframeworks examples I saw some which set texCoordVarying as a varying in the vertex shader, but this seems to only work when an actual image is input to the shader?

Would love to know the best way to do this - or if perhaps I’d need to include an image or FBO. Thanks!

#2

Hi @Alex_Z,

This is a classic! -faced the same problems myself :slight_smile: -.

You draw inside the the ofShader using the methods begin() and end().
If you are using an ofFbo and bind it (method1):

fboExample.getTextureReference().bind();
shader.begin();
fboExample.draw(0,0);
shader.end();

Then if you are using an ofFbo, and set a uniform texture ( method2):

shaderExample.begin();    
shaderExample.setUniformTexture("tex0", fboExample->getTextureReference(), 0);
fboExample.draw(0,0);
shaderExample.end();

the first texture which will be processed by your fragment shader will be the one corresponding to the Fbo. You can get it in the fragment shader by using :

uniform sampler2DRect tex0;

So to illustrate in your ofApp.h::setup():

void setup();
    void update();
    void draw();
    
    void keyPressed(int key);
    
    ofShader shader;
    ofImage img;
    ofFbo fboExample;
    
    bool useImg = true;
    bool useMethod1 = true;

ofApp.cpp::setup():

shader.load("shadersGL3/shader");
    img.load("img.jpg");
    fboExample.allocate(img.getWidth(), img.getHeight());

ofApp.cpp::update():

if(!useImg){
        ofPushStyle();
        fboExample.begin();
        ofClear(0);
        ofSetColor(0, 240, 233);
        ofDrawRectangle(0,0,img.getWidth(),img.getHeight() / 2);
        fboExample.end();
        ofPopStyle();
    }

ofApp.cpp::draw():

    if(useImg){
        // method 1 : use bind - unbind
        if(useMethod1)img.getTexture().bind();
        shader.begin();
        // method 2
        if(!useMethod1)shader.setUniformTexture("tex0", img.getTexture(), 0);
        img.draw(0,0);
        shader.end();
        if(useMethod1)img.getTexture().unbind();
        
    }else{
        // method 1 : use bind - unbind
        if(useMethod1)fboExample.getTextureReference().bind();
        shader.begin();
        // method 2
        if(!useMethod1)shader.setUniformTexture("tex0", fboExample.getTextureReference(), 0);
        fboExample.draw(0,0);
        shader.end();
        if(useMethod1)fboExample.getTextureReference().unbind();
        
    }

and ofApp::keyPressed(int key):

 if(key == ' ')useImg = !useImg;
    if(key == '1')useMethod1 = !useMethod1;

shader.vert look like this :

#version 150

// these are for the programmable pipeline system and are passed in
// by default from OpenFrameworks
uniform mat4 modelViewProjectionMatrix;

in vec4 position;
in vec2 texcoord;

// this is something we're creating for this shader
out vec2 texCoordVarying;

// this is coming from our C++ code
uniform float mouseX;

void main()
{
    // here we move the texture coordinates
    texCoordVarying = texcoord;

    // send the vertices to the fragment shader
	gl_Position = modelViewProjectionMatrix * position;
}

shader.frag look like :

#version 150

// this is how we receive the texture
uniform sampler2DRect tex0;

in vec2 texCoordVarying;

out vec4 outputColor;
 
void main()
{
    outputColor = texture(tex0, texCoordVarying);
}

This is all adapted from the example simpleTexture in the shader example folder, which I highly recommend you to look at.

Hope this helps /is clear.

++

P

1 Like
#3

Hi @pierre_tardif00, sorry for the delay, didn’t realize I had a reply here - thanks so much for the tips and your walkthrough!

It is helpful for my understanding of shaders and FBO’s, but still got another question as well, more pertaining to normalized uv fragment coordinates within a shader that draws a subsection of texture

Some pseudocode:


ofImage someImage; // image to be masked
ofImage imageMask; // image to be used as mask, not same size as someImage - ofDisableArbTex() before allocating this one
ofShader shaderExample;

// (allocate and load these images and the shader)

shaderExample.begin();
// shaderExample.setUniformTexture("tex0", otherImage.getTexture(), 0);  // redundant, this happens automatically
shaderExample.setUniformTexture("tex1", imageMask.getTexture(), 1);
shaderExample.setUniform2f("resolution", <widthOfSubsection>, <heightOfSubsection>);
otherImage.drawSubsection(<some subsection details>)
shaderExample.end();

(and in the vertex shader:)

...
out vec2 texCoordVarying;
...
...
     texCoordVarying = texcoord;
...

It works to call texture(tex0, texCoordVarying) in my fragment shader. I suppose the texcoord in the vertex shader corresponds with the non-normalized, arb rect texels of the texture subsection being drawn within my shader’s begin() and end() calls. This texCoordVarying for an image.drawSubsection does not start at 0,0, but start at the subsection start for the tex0

In my fragment shader, I also want to read and stretch another texture (tex1, the mask texture), over this rendered subsection region. However, I don’t have normalized relative fragment uv coordinates of the texture to sample - since texCoordVarying doesn’t start at 0,0. I was hoping I could just sample tex1 with texture(tex1, texCoordVarying/resolution), but this doesn’t work since texCoordVarying starts at some offset

Do I have to always pass in the subsection offset in pixels as a separate uniform, in order to figure out which uv fragment of the subsection I am currently sampling from? Or is there a built in way in glsl for me to get the current fragment on a 0 to 1 scale?

Thanks again!

#4

Hi, a much more simple way is to use an ofPrimitivePlane.
ofApp.h

#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{
	public:
    void setup();
    void draw();
    void keyPressed(int key);
	
    
    ofShader shader;
    ofPlanePrimitive plane;
	void loadShader();
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup() {
	ofSetLogLevel(OF_LOG_VERBOSE);
	loadShader();
    plane.set(800, 600, 10, 10);
	//the plane's position is linked to its center, so in order to draw it in the middle of the screen we move it to the middle of the screen.
	plane.setPosition({ofGetWidth()/2, ofGetHeight()/2, 0.0f});
}
//--------------------------------------------------------------
void ofApp::draw() {
	
    shader.begin();
	
    plane.draw();
	
    shader.end();
	
}
//--------------------------------------------------------------
void ofApp::loadShader(){
	if(ofIsGLProgrammableRenderer()){
		
		if(shader.load("shadersGL3/shader")){
			cout << "gl3 shader loaded " << endl;
		}
	}else{
		shader.load("shadersGL2/shader");
	}

}
//--------------------------------------------------------------
void ofApp::keyPressed(int key){
	if(key == ' ') loadShader();
}

shader.frag

#version 150

in vec2 texCoordVarying;

out vec4 outputColor;
 
void main(){

	outputColor = vec4(texCoordVarying, 1.0,1.0);
    
}

shader.vert

#version 150

// these are for the programmable pipeline system and are passed in
// by default from OpenFrameworks
uniform mat4 modelViewProjectionMatrix;

in vec4 position;
in vec2 texcoord;

// this is something we're creating for this shader
out vec2 texCoordVarying;


void main()
{
    // here we move the texture coordinates
    texCoordVarying = texcoord;

    // send the vertices to the fragment shader
	gl_Position = modelViewProjectionMatrix * position;
}

This example should give you a gradient like this