I am attempting to render a texture to a simple shape using QBuilder and QGeometryData but I can not get the texture to display, what am I doing wrong?
I have created a QGLSceneNode which builds the texture and attempts to use generateTextureCoordinates to map the texture coordinates.
EDIT
Mapping the texture coordinates by hand seems to work, but generateTextureCoordinates should work.
[My class is a child of QGLView]
vtkQtViewer::vtkQtViewer(QWidget *parent) :
QGLView(parent), scene(new QGLSceneNode(this))
{
// in the constructor construct a builder on the stack
QGLBuilder builder;
QGeometryData triangle;
QVector3D a(2, 2, 0);
QVector3D b(-2, 2, 0);
QVector3D c(2, -2, 0);
QVector3D d(-2, -2, 0);
triangle.appendVertex(a,b,c);
triangle.appendVertex(b,d,c);
triangle.generateTextureCoordinates();
// When adding geometry,
//QGLBuilder automatically creates lighting normals
builder << triangle;
// obtain the scene from the builder
QGLSceneNode* can = builder.finalizedSceneNode();
// apply effects at app initialization time
QGLMaterial *mat = new QGLMaterial;
mat->setDiffuseColor(Qt::red);
QUrl url;
url.setPath(QLatin1String("qtlogo.png"));
url.setScheme(QLatin1String("file"));
mat->setTextureUrl(url);
can->setMaterial(mat);
scene->addNode(can);
}
vtkQtViewer::~vtkQtViewer()
{
delete scene;
}
void vtkQtViewer::initializeGL(QGLPainter *painter)
{
painter->setStandardEffect(QGL::LitDecalTexture2D);
}
void vtkQtViewer::paintGL(QGLPainter *painter)
{
scene->draw(painter);
}}
Related
I'm writing a FFGL video effect plugin (using this shadertoy port). I want to store a previously rendered frame to use in a future calculation. Specifically I am trying to make the equivalent of a video delay.
Is it possible to write to an external buffer from a fragment shader and then use that stored value at a later time (say, 30 frames later)?
you create a FBO, and bind it. whatever you render next goes onto it. so if you're using a shader, that still applies like on the default FBO.
here's some code to help you out
import java.nio.ByteBuffer;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL14.*;
import static org.lwjgl.opengl.GL30.*;
public class FBO {
public int fbo,tex,depth;
public void delete() {glDeleteTextures(tex);glDeleteRenderbuffers(depth);glDeleteFramebuffers(fbo);}
public void bindTexture() {glBindTexture(GL_TEXTURE_2D,tex);}
public void bind() {glBindFramebuffer(GL_FRAMEBUFFER,fbo);}
public static void unbind() {glBindFramebuffer(GL_FRAMEBUFFER,0);}
public FBO(){ // create the fbo
glBindTexture(GL_TEXTURE_2D,tex=glGenTextures()); // create texture, set correct filters
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
glBindRenderbuffer(GL_RENDERBUFFER,depth=glGenRenderbuffers()); // create buffer for depth
glBindFramebuffer(GL_FRAMEBUFFER,fbo=glGenFramebuffers()); // create framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,tex,0); // attach texture
glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,depth); // attach depth
unbind(); // incase not using immediately
}
public void resize(int w,int h) {
glBindTexture(GL_TEXTURE_2D,tex); // update texture size
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,(ByteBuffer)null);
glBindRenderbuffer(GL_RENDERBUFFER,depth); // update depth size
glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT24,w,h);
}
public void render() {
// glColor4f(1,1,1,1); // you may want to do these customly
// glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, tex);
Tess.begin();
Tess.vertex( 0, height, 0, 0, 0);
Tess.vertex(width, height, 0, 1, 0);
Tess.vertex(width, 0, 0, 1, 1);
Tess.vertex( 0, 0, 0, 0, 1);
Tess.draw();
// Tess btw, just manually handles verts and tex coords
// that could be pure pipeline, or by vbo / vao / interleaved vbo
// width / height are just the 2d size. could render for post processing
}
}
I apologize if this isn't exact. I'm doing the best I can to copy code by hand from one computer to another, and the destination computer doesn't have a compiler (don't ask).
Header file
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <qopenglwidget.h>
class MyOpenGlWidget : public QOpenGLWidget
{
Q_OBJECT
public:
explicit MyOpenGlWidget(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags());
virtual ~MyOpenGlWidget();
protected:
// these are supposed to be overridden, so use the "override" keyword to compiler check yourself
virtual void initializeGL() override;
virtual void resizeGL(int w, int h) override;
virtual void paintGL() override;
private:
QPixmap *_foregroundPixmap;
}
#endif
Source file
QOpenGLFunctions_2_1 *f = 0;
MyOpenGlWidget::MyOpenGlWidget(QWidget *parent, Qt::WindowFlags f) :
QOpenGLWidget(parent, f)
{
_foregroundPixmap = 0;
QPixmap *p = new QPixmap("beveled_texture.tiff");
if (!p->isNull())
{
_foregroundPixmap = p;
}
}
MyOpenGlWidget::~MyOpenGlWidget()
{
delete _foregroundPixmap;
}
void MyOpenGlWidget::initializeGL()
{
// getting a deprecated set of functions because such is my work environment
// Note: Also, QOpenGLWidget doesn't support these natively.
f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // clearing to green
f->glEnable(GL_DEPTH_TEST);
f->glEnable(GL_CULL_FACE); // implicitly culling front face
f->glEnable(GL_SCISSOR_TEST);
// it is either copy the matrix and viewport code from resizeGL or just call the method
this->resizeGL(this->width(), this->height());
}
void MyOpenGlWidget::resizeGL(int w, int h)
{
// make the viewport square
int sideLen = qMin(w, h);
int x = (w - side) / 2;
int y = (h - side) / 2;
// the widget is 400x400, so this random demonstration square will show up inside it
f->glViewport(50, 50, 100, 100);
f->glMatrixMode(GL_PROJECTION);
f->glLoadIdentity();
f->glOrtho(-2.0f, +2.0f, -2.0f, +2.0f, 1.0f, 15.0f); // magic numbers left over from a demo
f->glMatrixMode(GL_MODELVIEW);
// queue up a paint event
// Note: QGLWidget used updateGL(), but QOpenGLWidget uses update().
this->update();
}
void MyOpenGlWidget::paintGL()
{
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// I want to draw a texture with beveled edges the size of this widget, so I can't
// have the background clearing all the way to the edges
f->glScissor(50, 50, 200, 200); // more magic numbers just for demonstration
// clears to green in just scissored area (unless QPainter is created)
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
// loading identity matrix, doing f->glTranslatef(...) and f->glRotatef(...)
// pixmap loaded earlier in another function
if (_foregroundPixmap != 0)
{
// QPixmap apparently draws such that culling the back face will cull the entire
// pixmap, so have to switch culling for duration of pixmap drawing
f->glCullFace(GL_FRONT);
QPainter(this);
painter.drawPixmap(0, 0, _foregroundPixmap->scaled(this->size()));
// done, so switch back to culling the front face
f->glCullFace(GL_BACK);
}
QOpenGLFunctions_2_1 *f = 0;
void MyOpenGlWidget::initializeGL()
{
// getting a deprecated set of functions because such is my work environment
// Note: Also, QOpenGLWidget doesn't support these natively.
f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // clearing to green
f->glEnable(GL_DEPTH_TEST);
f->glEnable(GL_CULL_FACE); // implicitly culling front face
f->glEnable(GL_SCISSOR_TEST);
// it is either copy the matrix and viewport code from resizeGL or just call it directly
this->resizeGL(this->width(), this->height());
}
void MyOpenGlWidget::resizeGL(int w, int h)
{
// make the viewport square
int sideLen = qMin(w, h);
int x = (w - side) / 2;
int y = (h - side) / 2;
// the widget is 400x400, so this random demonstration square will show up inside it
f->glViewport(50, 50, 100, 100);
f->glMatrixMode(GL_PROJECTION);
f->glLoadIdentity();
f->glOrtho(-2.0f, +2.0f, -2.0f, +2.0f, 1.0f, 15.0f); // magic numbers left over from a demo
f->glMatrixMode(GL_MODELVIEW);
// queue up a paint event
// Note: QGLWidget used updateGL(), but QOpenGLWidget uses update().
this->update();
}
void MyOpenGlWidget::paintGL()
{
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// I want to draw a texture with beveled edges the size of this widget, so I can't
// have the background clearing all the way to the edges
f->glScissor(50, 50, 200, 200); // more magic numbers just for demonstration
// clears to green in just scissored area (unless QPainter is created)
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
// loading identity matrix, doing f->glTranslatef(...) and f->glRotatef(...), drawing triangles
// done drawing, so now draw the beveled foreground
if (_foregroundPixmap != 0)
{
// QPixmap apparently draws such that culling the back face will cull the entire
// pixmap, so have to switch culling for duration of pixmap drawing
f->glCullFace(GL_FRONT);
QPainter(this);
painter.drawPixmap(0, 0, _foregroundPixmap->scaled(this->size()));
// done, so switch back to culling the front face
f->glCullFace(GL_BACK);
}
}
The problem is this code from paintGL():
QPainter(this);
As soon as a QPainter object is created, the glScissor(...) call that I made earlier in the function is overrun and some kind of glClearColor(...) call is made (possibly from QPainter's constructor) that clears the entire viewport to the background color that I set just after glScissor(...). Then the pixmap draws my beveled texture just fine.
I don't want QPainter to overrun my scissoring.
The closest I got to an explanation was two QPainter methods, beginNativePainting() and endNativePainting(). According to the documentation, scissor testing is disabled between these two, but in their example they re-enable it. I tried using this "native painting" code, but I couldn't stop QPainter's mere existence from ignoring GL's scissoring and clearing my entire viewport.
Why is this happening and how do I stop this?
Note: This work computer has network policies to prevent me from going to entertainment sites like imgur to upload "what I want" and "what I get" pictures, so I have to make due with text.
Why is this happening
The OpenGL context is a shared resource and you have to share it with other players.
and how do I stop this?
You can't. Just do the proper thing and set viewport, scissor rectangle and all the other drawing related state at the right moment: Right before you are going to draw something that relies on these settings. Don't set them aeons (in computer terms) before, somewhere in some "initialization" or a reshape handler. And be expected that in drawing code any function you call that makes use of OpenGL will leave some garbage behind.
I'm trying to use a lightmap to create an effect like this, but doing it only with blending functions and rendering: https://www.youtube.com/watch?v=JkyGTZ0992s
Basically, have the light map have 100% transparency when it's all white and have it fade off from there, as you can see in the video above.
Right now, it's blending, but it's very dark as you can see from the picture (ignore starbound art): https://gyazo.com/fa0cecbe6085db3dc15291a86396af13
Is this because of my blending modes? I can't figure it out, hopefully you can! Thanks!
CODE:
public ShaderProgram shader;
public Texture texture;
public Sprite sprite;
public Sprite lightSprite;
public Texture lightText;
public ShapeRenderer shape;
SpriteBatch spriteBatch;
#Override
public void create() {
shape = new ShapeRenderer();
spriteBatch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("image.png"));
sprite = new Sprite(texture);
lightText = new Texture(Gdx.files.internal("light.png"));
lightSprite = new Sprite(lightText);
ShaderProgram.pedantic = false;
shader = new ShaderProgram(Gdx.files.internal("shaders/passthrough.vert"), Gdx.files.internal("shaders/passthrough.frag"));
System.out.println(shader.getLog());
spriteBatch.setShader(shader);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
/*
Draw image
*/
spriteBatch.setShader(shader);
spriteBatch.begin();
sprite.draw(spriteBatch);
spriteBatch.setShader(null);
spriteBatch.setBlendFunction(GL30.GL_DST_COLOR, GL30.GL_SRC_ALPHA);
lightSprite.draw(spriteBatch, 1.0f);
spriteBatch.end();
lightSprite.setPosition(Gdx.input.getX(), Gdx.input.getY());
spriteBatch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
}
}`
The shader I'm using just makes the screen darker, here that is too:
VERT: http://pastebin.com/LkxCZGf6
FRAG: http://pastebin.com/dLwudnHx
lightmap:
I'm using glClearColor inside begin() and end() in framebuffer,
but it is clearing the whole screen color, Am I doing something wrong?
public class FrameBufferTest implements ApplicationListener{
OrthographicCamera camera;
SpriteBatch batcher;
FrameBuffer fbo;
Texture tex;
#Override
public void create() {
batcher = new SpriteBatch();
camera = new OrthographicCamera(800, 480);
camera.position.set(camera.viewportWidth/2f, camera.viewportHeight/2f, 0);
fbo = new FrameBuffer(Format.RGBA8888, 100, 100,false);
camera.position.set(camera.viewportWidth/2f, camera.viewportHeight/2f, 0);
tex = new Texture("data/bg.png");
}
#Override
public void render() {
GL20 gl = Gdx.graphics.getGL20();
gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
gl.glClearColor(0.5f,0.5f,0.5f,0.5f); // grey color
camera.update();
batcher.setProjectionMatrix(camera.combined);
batcher.enableBlending();
batcher.begin();
batcher.draw(tex, 0, 0, 256, 256);
batcher.end();
fbo.begin();
gl.glClearColor(1f,0,0,1f);
gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
fbo.end();
batcher.begin();
batcher.draw(fbo.getColorBufferTexture(), 512, 0, 100, 100);
batcher.end();
}
Swap these two lines at the beginning of your render method:
gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
gl.glClearColor(0.5f,0.5f,0.5f,0.5f); // grey color
In the order you have them, it is clearing the color using the most recently set clear color, which is the red that you set farther down in your render method (since this is a loop).
I am using scene2d. Here is my code:
group.addActor(new Actor() {
#Override
public Actor hit(float arg0, float arg1) {
return null;
}
#Override
public void draw(SpriteBatch batch, float arg1) {
batch.end();
shapeRenderer.begin(ShapeType.FilledRectangle);
shapeRenderer.setColor(Color.RED);
shapeRenderer.filledRect(0, 0, 300, 20);
shapeRenderer.end();
batch.begin();
}
});
The problem is that it draws this rectangular relative to screen (x = 0, y = 0), but I need it to be drawn relative to my group. But if I draw other entities with:
batch.draw(texture, 0, 0, width, height);
it correctly draws at (x = 0, y = 0) relative my group (0,0 pixels from left-bottom corner of the group).
Any suggestions how can I implement shape drawing in scene2d? And can someone can explain why these two calls work differently?
ShapeRenderer has its own transform matrix and projection matrix. These are separate to those in the SpriteBatch that the scene2d Stage uses. If you update the ShapeRenderer's matrices to match those that scene2d is using when Actor.draw() is called then you should get the results that you want.
As Rod Hyde mentions, ShapeRenderer has its own transform matrix and projection matrix.
So you would have to get the SpriteBatch's projection Matrix first.
I am not sure if there is an elegant way to do it, I did it like this:
public class myActor extends Actor{
private ShapeRenderer shapeRenderer;
static private boolean projectionMatrixSet;
public myActor(){
shapeRenderer = new ShapeRenderer();
projectionMatrixSet = false;
}
#Override
public void draw(SpriteBatch batch, float alpha){
batch.end();
if(!projectionMatrixSet){
shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix());
}
shapeRenderer.begin(ShapeType.Filled);
shapeRenderer.setColor(Color.RED);
shapeRenderer.rect(0, 0, 50, 50);
shapeRenderer.end();
batch.begin();
}
}
The best solution for me. Cause when you using ShapeRenderer it's doesn't react on moving/zooming camera.
public class Rectangle extends Actor {
private Texture texture;
public Rectangle(float x, float y, float width, float height, Color color) {
createTexture((int)width, (int)height, color);
setX(x);
setY(y);
setWidth(width);
setHeight(height);
}
private void createTexture(int width, int height, Color color) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
pixmap.fillRectangle(0, 0, width, height);
texture = new Texture(pixmap);
pixmap.dispose();
}
#Override
public void draw(Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}
}
You need to convert the Actor's local coordinates into screen coordinates. Assuming your stage is full-screen, you can just use Actor.localToStageCoordinates:
vec.set(getX(), getY());
this.localToStageCoordinates(/* in/out */ vec);
shapeRenderer.filledRect(vec.x, vec.y, getWidth(), getHeight());
Where vec is a private Vector2d (you don't want to allocate a new one on each render call).
This is also assuming that your ShapeRenderer is defined to be map to the full screen (which is the default).
Also, if you switch away from the ShapeRenderer and back to the SpriteBatch, note that the batch is already adjusted to Actor coordinates (and thus you can use getX() and getY() directly with batch.draw(...).
If your are using the ShapeRenderer don't forget using setProjectionMatrix() and setTransformMatrix() methods...
A sample of draw circle inside an Actor on draw method :
#Override public void draw(Batch batch, float parentAlpha) {
batch.end();
if (shapeRenderer == null) {
shapeRenderer = new ShapeRenderer();
}
Gdx.gl.glEnable(GL20.GL_BLEND);
shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix());
shapeRenderer.setTransformMatrix(batch.getTransformMatrix());
shapeRenderer.setColor(mColor.r, mColor.g, mColor.b, mColor.a * parentAlpha);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.circle(getX() + getWidth()/2 , getY() + getHeight()/2 , Math.min(getWidth(),getHeight())/2 );
shapeRenderer.end();
Gdx.gl.glDisable(GL20.GL_BLEND);
batch.begin();
}