I'm new at OpenGL and glsl and trying to use it on RPI2.
I did my first practice example on OpenGL 2.1 and glsl version120. Now I'm working on OpenGLES 2.0.
What I want to do is that store float values to a texture and send it to vertex shader to make lines.
So, I made one array for float values to store, and passing these values to one FBO. And I send this FBO to UniformTexture. But It seems vertex shader doesn't get any values.
OpenGL ES 2.0 document states that "Texture lookup functions are available to both vertex and fragment shaders." Also I have checked GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS in RPI2 and it returns 8.
Unfortunately, I cannot fetch any values form texture that I send to vertex shader.
What am I missing here?
I posted this question at openframeworks forum as well.
Any help would be appreciated. I really hope.
Thanks !
vertex shader code
precision highp float;
uniform mat4 modelViewProjectionMatrix;
uniform sampler2D valTex;
uniform vec2 spaceSize;
attribute vec4 position;
attribute vec2 texcoord;
void main() {
vec4 pixPos = position;
vec2 valAmt = texture2D(valTex, texcoord).xy;
valAmt.x *= spaceSize.x;
valAmt.y *= spaceSize.y;
vec2 nPos;
nPos.x = (pixPos.x + valAmt.x);
nPos.y = (pixPos.y + valAmt.y);
if ((v_pos.x <= 1.0)) {
gl_Position = modelViewProjectionMatrix * vec4(pixPos.x, pixPos.y, 0, 1);
}else{
gl_Position = modelViewProjectionMatrix * vec4(nPos.x, nPos.y, 0, 1);
}
}
Fragment shader
void main() {
gl_FragColor = vec4(1.0, 1.0, 0.5, 1.0);
}
OFX CODE
#include "ofApp.h"
void ofApp::setup(){
string ShaderFolder;
ShaderFolder = "shader_gles";
renderShader.load(ShaderFolder + "/fluidRender.vert", ShaderFolder + "/fluidRender.frag");
renderFbo.allocate(spaceSize, spaceSize, GL_RGB); //Render to Screen
renderFbo.begin();
ofClear(0);
renderFbo.end();
valTex.allocate(gridSize, gridSize, GL_RGB); //Store values from array
valTex.begin();
ofClear(0);
valTex.end();
int vertexNum = gridSize * gridSize;
vector<float> val(vertexNum * 3); //Store values
for (int x = 0; x < gridSize; x++) {
for (int y = 0; y < gridSize; y++) {
val[((x * 3 * gridSize) + y * 3) + 0] = 200.0; //x pos value
val[((x * 3 * gridSize) + y * 3) + 1] = 200.0; //y pos value
val[((x * 3 * gridSize) + y * 3) + 2] = 0.0;
}
}
valTex.getTexture().loadData(val.data(), gridSize, gridSize, GL_RGB);
mesh.setMode(OF_PRIMITIVE_LINES);
for (int x = 0; x < gridSize; x++) {
for (int y = 0; y < gridSize; y++) {
mesh.addVertex(ofVec3f(x * cellSize, y * cellSize)); //center vertex
mesh.addTexCoord(ofVec2f(1.1, 1.1));
mesh.addVertex(ofVec3f(x * cellSize, y * cellSize)); //val vertex
mesh.addTexCoord(ofVec2f(x / gridSize - 1, y / gridSize - 1)); //normalize texcoord
mesh.addIndex((x * 2) * gridSize + (y * 2) + 0);
mesh.addIndex((x * 2) * gridSize + (y * 2) + 1);
}
}
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &vertex_texture_units);
}
void ofApp::update(){
vertexRender();
}
void ofApp::draw(){
ofBackground(0);
renderFbo.draw(0, 0);
ofDrawBitmapString("v_tex_unit : " + ofToString(vertex_texture_units), 15, 15);
}
void ofApp::vertexRender(){
renderFbo.begin();
ofClear(0);
renderShader.begin();
renderShader.setUniformTexture("velTex", valTex.getTexture(), 0);
renderShader.setUniform2f("spaceSize", spaceSize, spaceSize);
mesh.draw();
renderShader.end();
renderFbo.end();
}
NOTE
Not GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS in description. It is GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS in the code.
EDIT
This screen shot below is what I want to draw using vertex shader. Currently in this case, I send mouse x and y position float value (0 to screen size which is 960.0 in this case) to uniform to display value vertex.
Each line has two vertex. One has fixed position which is from 'addVertex(x,y)' function.
mesh.addVertex(ofVec3f(x * cellSize, y * cellSize));
Another one has 'fixed position value + mouse position value'. So If mouse moves, It makes a line connecting fixed position vertex and 'fixed position + mouse position' vertex.
renderShader.setUniform2f("mousePos", mouseX, mouseY);
in GLSL
vec2 pixPos = position;
vec2 nPos;
nPos.x = (pixPos.x + mousePos.x);
nPos.y = (pixPos.y + mousePos.y);
vertex_mouse_position //<-sccreen shot (I can't embed pictures directly yet. sorry.)
Another picture is when I send texture(FBO) to vertex shader. I made an array and set 200.0 float value for each x, y position and load this array to FBO.
vector<float> val(vertexNum * 3); //Store values
for (int x = 0; x < gridSize; x++) {
for (int y = 0; y < gridSize; y++) {
val[((x * 3 * gridSize) + y * 3) + 0] = 200.0; //x pos value
val[((x * 3 * gridSize) + y * 3) + 1] = 200.0; //y pos value
val[((x * 3 * gridSize) + y * 3) + 2] = 0.0;
}
}
valTex.getTexture().loadData(val.data(), gridSize, gridSize, GL_RGB);
then send this FBO to vertex shader.
renderShader.setUniformTexture("velTex", valTex.getTexture(), 0);
But, I just get black screen displaying noting.
vertex_texture
I hope this help you to more understanding for my issue. Thank for reply. I really appreciate.
You are checking the wrong query - check GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS.
On most GLES 2.0 implementations this will return zero; i.e. vertex texturing not supported. It's mandatory in GLES 3.x.
Related
I have a very simple glsl vertex shader, which is the following:
#version 430
in vec2 vertices;
in vec2 textures;
out vec2 tex_coords;
uniform mat4 projection;
uniform mat4 texModifier;
void main() {
tex_coords = (texModifier * vec4(textures,0,1)).xy;
gl_Position = projection * vec4(vertices, 0, 1);
}
This shader is used to render text. The text is just a font sheet where I change the texModifier so that the mat4 coordinates are over the specific letter I want to draw. This works fine, but I noticed that different projection matrixes cause different visual artifacts.
Here is the an example:
This is the text using its original position.
And this is the the same text only the projection matrix is translated 0.1f / 1920 to the left. The visual artifacts change from being around commas and r to being around the u, S and more. These artifacts appear even when using linear interpolation.
This is the function used to draw text:
public void drawText(Shader shader, CharSequence text, float x, float y, int letters) {
if(letters == -1)
letters = text.length();
Model model;
Matrix4f projection = new Matrix4f();
Texture fontSheet = texture;
int textHeight = getTextHeight(text);
projection.setOrtho(-1, 1, -1, 1, -1, 1);
float drawX = x;
float drawY = y;
if (textHeight > fontHeight) {
drawY += textHeight - fontHeight;
}
shader.bind();
shader.setUniform("sampler", 0);
fontSheet.bind(0);
for (int i = 0; i < text.length() && i < letters; i++) {
char ch = text.charAt(i);
if (ch == '\n') {
/* Line feed, set x and y to draw at the next line */
drawY -= fontHeight;
drawX = x;
continue;
}
if (ch == '\r') {
/* Carriage return, just skip it */
continue;
}
Glyph g = glyphs.get(ch);
model = Model.createQuad((float) g.width / 2, (float) g.height / 2);
projection.setTranslation(((float) g.width / 2 + drawX) / Main.WINDOW_WIDTH, drawY / Main.WINDOW_HEIGHT, 0);
Matrix4f texModifier =
new Matrix4f()
.translate((float) g.x / fontSheet.getWidth(), 0, 0)
.scale((float) g.width / fontSheet.getWidth(), -(float) g.height / fontSheet.getHeight(), 1);
shader.setUniform("projection", projection);
shader.setUniform("texModifier", texModifier);
model.render();
drawX += g.width;
}
}
Why is the projection causing theses artifacts?
I'm trying to render an ellipse where I can decide how hard the edge is.
(It should also be tileable, i.e. I must be able to split the rendering into multiple textures with a given offset)
I came up with this:
float inEllipseSmooth(vec2 pos, float width, float height, float smoothness, float tiling, vec2 offset)
{
pos = pos / iResolution.xy;
float smoothnessSqr = smoothness * tiling * smoothness * tiling;
vec2 center = -offset + tiling / 2.0;
pos -= center;
float x = (pos.x * pos.x + smoothnessSqr) / (width * width);
float y = (pos.y * pos.y + smoothnessSqr) / (height * height);
float result = (x + y);
return (tiling * tiling) - result;
}
See here (was updated after comment -> now it's how I needed it):
https://www.shadertoy.com/view/ssGBDK
But at the moment it is not possible to get a completely hard edge. It's also smooth if "smoothness" is set to 0.
One idea was "calculating the distance of the position to the center and comparing that to the corresponding radius", but I think there is probably a better solution.
I was not able to find anything online, maybe I'm just searching for the wrong keywords.
Any help would be appreciated.
I don't yet understand what you are trying to accomplish.
Anyway, I have been playing with shadertoy and I have created something that could help you.
I think that smoothstep GLSL function is what you need. And some inner and outer ratio to set the limits of the inner and border.
It is not optimized...
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
int tiling = 4;
float width = 0.5;
float height = 0.2;
float smoothness = 0.9;
float outerRatio = 1.0;
float innerRatio = 0.75;
vec2 offset = vec2(0.25, 0.75);
//offset = iMouse.xy / iResolution.xy;
vec2 center = vec2(0.5, 0.5);
vec2 axis = vec2(width, height);
vec2 pos = float(tiling) * (fragCoord.xy);
pos = mod(pos / iResolution.xy, 1.0);
pos = mod(pos - offset, 1.0);
pos = pos - center;
pos = (pos * pos) / (axis * axis);
float distance = pos.x + pos.y;
float alpha;
if ( distance > outerRatio ) { alpha = 0.0; }
else if ( distance < innerRatio ) { alpha = 1.0; }
else { alpha = smoothstep(outerRatio, innerRatio, distance); }
fragColor = vec4(vec3(alpha), 1.0);
}
Shadertoy multiple ellipses with soft edge and solid inner
There are two draw object groups that I'd like to combine into one canvas, a rain effect layer and points forming a circle. These are their vertex shader codes, taken from this website
// Rain effect
const vs = `#version 300 es
uniform int numVerts;
uniform float time;
// hash function from https://www.shadertoy.com/view/4djSRW
// given a value between 0 and 1
// returns a value between 0 and 1 that *appears* kind of random
float hash(float p) {
vec2 p2 = fract(vec2(p * 5.3983, p * 5.4427));
p2 += dot(p2.yx, p2.xy + vec2(21.5351, 14.3137));
return fract(p2.x * p2.y * 95.4337);
}
void main() {
float u = float(gl_VertexID) / float(numVerts); // goes from 0 to 1
float off = floor(time + u) / 1000.0; // changes once per second per vertex
float x = hash(u + off) * 2.0 - 1.0; // random position
float y = fract(time + u) * -2.0 + 1.0; // 1.0 -> -1.0
gl_Position = vec4(x, y, 0, 1);
gl_PointSize = 2.0;
}
`;
// Points forming a circle
const vs = `#version 300 es
uniform int numVerts;
uniform vec2 resolution;
#define PI radians(180.0)
void main() {
float u = float(gl_VertexID) / float(numVerts); // goes from 0 to 1
float angle = u * PI * 2.0; // goes from 0 to 2PI
float radius = 0.8;
vec2 pos = vec2(cos(angle), sin(angle)) * radius;
float aspect = resolution.y / resolution.x;
vec2 scale = vec2(aspect, 1);
gl_Position = vec4(pos * scale, 0, 1);
gl_PointSize = 5.0;
}
`;
Both shaders assign different values to gl_Position and gl_PointSize. I couldn't think of a way to combine them under one vertex shader without conflict. It doesn't help that the vertex dataset is entirely generated on site, inside the shader instead of having it passed from the buffer.
Add a uniform variable to the shader that indicates which algorithm to use.
#version 300 es
uniform int numVerts;
uniform float time;
uniform int mode; // 0 or 1
// hash function
// [...]
void main() {
float u = float(gl_VertexID) / float(numVerts); // goes from 0 to 1
vec2 pos;
float pointSize;
if (mode == 0) {
float off = floor(time + u) / 1000.0; // changes once per second per vertex
float x = hash(u + off) * 2.0 - 1.0; // random position
float y = fract(time + u) * -2.0 + 1.0; // 1.0 -> -1.0
pos = vec2(x, y);
pointSize = 2.0
}
else {
float angle = u * PI * 2.0; // goes from 0 to 2PI
float radius = 0.8;
float aspect = resolution.y / resolution.x;
vec2 scale = vec2(aspect, 1);
pos = vec2(cos(angle), sin(angle)) * radius * scale;
pointSize = 5.0;
}
gl_Position = vec4(pos, 0, 1);
gl_PointSize = pointSize;
}
I want to ask a question about my lighting effect in OpenGL.
I am trying to add lighting, but I don't think it's good and I've seen some 2D lighting pictures which are so much better than mine.
Question: I have made a spotlight but I want it to be dimmer as its light range gets lower and have it more like a natural light, but I can't figure out the solution.
I am using an orthographic matrix with (800, 600) as the window size and I make my meshes with real x, y coords. I send my lightPos and my PlayerPos to the fragment shader and I use the vertex as the width and the height of the mesh so that I can generate lighting for every pixel.
The light is just a basic circle and I don't know how to make it look better. Here are some images. In the fragment shader, I use the Pythagorean Theorem to calculate the distance between the 2 points.
And here is the vertex and fragment Shader
Vetex shader
#version 330
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 tCoord;
uniform mat4 mat;
out vec2 tCoord0;
out vec2 vPos;
void main(){
tCoord0 = vec2(tCoord.x, 1 - tCoord.y);
gl_Position = mat * vec4(pos, 1.0);
vPos = vec2(pos.x, pos.y);
}
Fragment shader
#version 330
out vec4 color;
uniform sampler2D sampler;
in vec2 tCoord0;
uniform vec3 objColor;
uniform vec2 lightPos;
uniform vec2 xyPos;
in vec2 vPos;
void main(){
vec4 textureColor = texture2D(sampler, tCoord0);
vec3 ambientLight = vec3(0.3f, 0.3f, 0.3f);
float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
if(dist > 0 && dist < 50){
ambientLight = vec3(0.7f, 0.7f, 0.7f) * 0.6f;
}
else if(dist > 50 && dist < 70){
ambientLight = vec3(0.4f, 0.4f, 0.4f) * 0.6f;
}
else{
discard;
}
if((textureColor.x == 0 && textureColor.y == 0 && textureColor.z == 0) || textureColor.a <= 0){
color = vec4(objColor, 1.0) * vec4(ambientLight, 1.0);
}
else{
color = textureColor * vec4(ambientLight, 1.0) * vec4(objColor, 1.0);
}
}
Drawer.cpp
#include <graphics\shader.h>
#include <graphics\texture.h>
#include <graphics\shape.h>
#include <GL\glew.h>
#include <graphics\light.h>
#include <core\TSAContainer.h>
#include <core\drawer.h>
namespace GE{
namespace core{
std::vector<graphics::GraphicComponent*> Drawer::drawables;
GLuint Drawer::buffer;
void Drawer::init(){
glGenFramebuffers(1, &buffer);
}
std::vector<graphics::GraphicComponent*>& Drawer::getAllGraphicComponents(){
return drawables;
}
void Drawer::addDrawable(graphics::GraphicComponent* drawable){
drawables.push_back(drawable);
}
void Drawer::destroy(){
for (unsigned int i = 0; i < drawables.size(); i++)
delete drawables[i];
drawables.clear();
}
void Drawer::render(){
for (std::vector<graphics::GraphicComponent*>::iterator it = drawables.begin(); it != drawables.end(); it++){
if ((*it)->isDraw()){
(*it)->getShader().bind();
int color = getColor(static_cast<graphics::Shape*>(*it)->getColor());
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = (color)& 0xff;
(*it)->getShader().setUniform("mat", (*it)->getTransformation().getTransformationMatrix());
(*it)->getShader().setUniform("objColor", r, g, b);
(*it)->getShader().setUniform("xyPos", (*it)->getTransformation().getPosition());
(*it)->getShader().setUniform("sampler", 1);
if (static_cast<graphics::Shape*>(*it)->getLight() != NULL){
static_cast<graphics::Shape*>(*it)->getLight()->update();
}
//(*it)->getShader().setUniform("ambientLight", static_cast<graphics::Shape*>(*it)->getAmbientLight());
glActiveTexture(GL_TEXTURE1);
if ((*it)->getTexture() != NULL)
(*it)->getTexture()->bind();
(*it)->getMesh().draw();
if ((*it)->getTexture() != NULL)
(*it)->getTexture()->unbind();
(*it)->getShader().unbind();
}
}
}
int Drawer::getColor(colorType color){
int col = 0;
if (color == GE_COLOR_BLUE){
col = 0 << 16 | 0 << 8 | 1;
}
else if (GE_COLOR_GREEN == color){
col = 0 << 16 | 1 << 8 | 0;
}
else if (GE_COLOR_RED == color){
col = 1 << 16 | 0 << 8 | 0;
}
else{
col = 1 << 16 | 1 << 8 | 1;
}
return col;
}
Drawer::Drawer(){
}
Drawer::~Drawer(){
}
}
}
float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
if(dist > 0 && dist < 50)
{
ambientLight = vec3(0.7f, 0.7f, 0.7f) * 0.6f;
}
else if(dist > 50 && dist < 70)
{
ambientLight = vec3(0.4f, 0.4f, 0.4f) * 0.6f;
}
Here you're using kind of a constant attenuation based on distance. That's going to make that kind of effect of a bright inner circle and dim outer circle with unnaturally hard edges between.
If you want a soft kind of gradient effect, you want to avoid the branching and constants here. We can start with a linear falloff:
float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
float max_dist = 70.0f;
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);
ambientLight = vec3(percent, percent, percent);
However, that will probably look kind of ugly to you with a sharp point around the center. We can use an exponential curve instead, like so:
...
percent *= percent;
ambientLight = vec3(percent, percent, percent);
To make it kind of "rounder", you can multiply again:
...
percent *= percent * percent;
ambientLight = vec3(percent, percent, percent);
If that's kind of opposite of what you want visually, you can try sqrt:
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);
percent = sqrt(percent);
Since I don't know exactly what you're after visually, these are some things to try initially. Play with these two and see if you like what you get.
If you really want to take max control over the effect, a cubic bezier curve interpolation might come in handy:
float bezier4(float p1, float p2, float p3, float p4, float t)
{
const float mum1 = 1.0f - t;
const float mum13 = mum1 * mum1 * mum1;
const float mu3 = t * t * t;
return mum13 * p1 + 3 * t * mum1 * mum1 * p2 + 3 * t * t * mum1 * p3 + mu3 * p4;
}
...
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);
// Can play with the first four arguments to achieve the desired effect.
percent = bezier4(0.0f, 0.25f, 0.75f, 1.0f, percent);
ambientLight = vec3(percent, percent, percent);
That will give you a lot of control over the effect, but maybe overkill. Try the other methods first.
I want to do LOD in Tessenllation Control Shader. And my method is to calculate the area each patch occupyed on screen coordinate, and set different tessellation level for them.
So I need to access all vertices within a patch and I do so like:
for(int i = 0; i < 4; i++)
{
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
}
where i defined my patch in TCS like:
#version 400
layout( vertices=4 ) out;
and here is related codes in OpenGL:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rect_index_buffer);
glPatchParameteri(GL_PATCH_VERTICES, 4);
glDrawElements(GL_PATCHES, RECT_INDEX_SIZE, GL_UNSIGNED_INT, 0);
However the result is strange.The tessellation level is related to the area on the scrren, but all patches have the same tessellation level.
So what's the problem?
I guess it's the way I try getting access to vertices within a patch went wrong. Then how can I do that?
The following is codes in my Tessellation Control Shader, I hope it helps:
#version 400
layout( vertices=4 ) out;
uniform mat4 ProjectionMatrix;
uniform mat4 ModelViewMatrix;
uniform float window_height;
uniform float window_width;
float PI = 3.14159;
float calcTriangleArea(float l[3]) //Heron's formula
{
float p = (l[0] + l[1] + l[2]) / 2.0;
return sqrt(p * (p - l[0]) * (p - l[1]) * (p - l[2]));
}
float calcSqureArea(vec4 position[4])
{
vec2 position_screen[4];
for(int i=0;i<4;i++)
{
position_screen[i] = position[i].xy;
}
float l[4];
for(int i = 0;i < 4;i++)
{
l[i] = length(position_screen[(i + 1) % 4] - position_screen[i % 4]);
}
float diagonal = length(position_screen[2] - position_screen[0]);
float l1[3];
float l2[3];
l1[0] = l[0];
l1[1] = l[1];
l1[2] = diagonal;
l2[0] = l[2];
l2[1] = l[3];
l2[2] = diagonal;
float area = calcTriangleArea(l1) + calcTriangleArea(l2);
return area;
}
float checkInsideView(vec4 position[4]) //check if the patch is visible
{
int flag = 4;
for(int i=0;i<4;i++)
{
if((position[i].x >= -window_width / 2.0) && (position[i].x <= window_width / 2.0) &&
(position[i].y >= -window_height / 2.0) && (position[i].y <= window_height / 2.0))
{
flag --;
}
}
if(flag == 0) //all 4 vertices are visible
{
return 0.0;
}
else if(flag == 4) //not all visible
{
return 2.0;
}
else //all vertices are not visible
{
return 1.0;
}
}
float calcLODLevel()
{
vec4 position_screen[4];
for(int i = 0; i < 4; i++)
{
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
}
float in_view_level = checkInsideView(position_screen);
//tess number is decided by the area that this patch covers on
//the screen
float area = calcSqureArea(position_screen);
float level = sqrt(area);
if(in_view_level == 1.0)
{
level /= sqrt(2);
}
//dont do tessellation
//if this patch is not visible
else if(in_view_level == 2.0)
{
level = 1.0;
}
return level;
}
void main()
{
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
float lod_level = calcLODLevel();
gl_TessLevelOuter[0] = lod_level;
gl_TessLevelOuter[1] = lod_level;
gl_TessLevelOuter[2] = lod_level;
gl_TessLevelOuter[3] = lod_level;
gl_TessLevelInner[0] = lod_level;
gl_TessLevelInner[1] = lod_level;
}
I think the problem is with your calculation of the screen coordinates, resulting in the tessellation levels to be too small. The key part is this:
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
What you're calculating here are clip coordinates, not screen coordinates. To get screen coordinates from clip coordinates, you need to:
Perform the perspective division. This gives you NDC (Normalized Device Coordinates) in the range [-1.0, 1.0].
Calculate screen coordinates from the NDC.
In code, the calculation could look like this:
vec4 posClip = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
vec2 posNdc = posClip.xy * (1.0 / posClip.w);
vec2 posScreen = 0.5 * (posNdc + 1.0) * vec2(window_width, window_height);