Related
I have been experimenting with 3d projection for a while,
but I've always used vertexes in -1 1 range I want to use units that are 1pixel big, so I use glm::ortho(-width / 2, width / 2, -height/2, height, 0.1f, 10.0f);
but that doesn't work
heres the code with glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, 0.1f,1.0f):
#include "Engine.h"
#include "vendor/stb/stb_image.h"
float width = 1200;
float height = 750;
// edits in imgui files have farouk comment before them
int main(void)
{
initGLFW();
GLFWimage icon;
icon.pixels = stbi_load("data/Images/Icon.png", &icon.width, &icon.height, nullptr, 4);
GLFWimage image;
image.pixels = stbi_load("data/Images/Cursor.png", &image.width, &image.height, nullptr, 4);
GLFWcursor * cursor = glfwCreateCursor(&image, image.width / 2, image.height / 2);
glfwWindowHint(GLFW_SAMPLES, 6);
glEnable(GL_MULTISAMPLE);
Window window("OGL", width, height, MVP());
Renderer renderer;
renderer.GenerateFonts({"data/fonts/arial.ttf", "data/fonts/Pixelboy.ttf"}, {"arial", "roboto"}, {64, 64});
// set perspective projection & set xyz ranges from -1, 1 to respective ranges
window.mvp.projection = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, 0.1f, 10.0f);
window.createWindow("data/Images/Cursor.png", "data/Images/Icon.png");
glfwSetWindowIcon(window.window, 1, &icon);
glfwSetCursor(window.window, cursor);
stbi_image_free(image.pixels);
stbi_image_free(icon.pixels);
glfwMakeContextCurrent(window.window);
initGLEW(true);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
renderer.loadFonts({"arial", "roboto"});
initImGUI(window.window, "data/fonts/arial.ttf");
Shader shader("data/shaders/base.shader");
// TODO: Add Multiple Window Support (Window Manager)
// Addd .txt file for text with Characters map insteadt loaf Fonts
// texture in 3d space & 3d textures
// map -1, 1 to -width/2 , width/2
std::vector <Obj> cubes = {
{
{
{ {-0.5, -0.5, -1.1}, {1,1,1,1} },
{ {-0.5, 0.5, -1.1}, {1,1,1,1} },
{ {0.5, 0.5, -1.1}, {1,1,1,1} },
{ {0.5, -0.5, -1.1}, {1,1,1,1} },
{ {-0.5, -0.5, -2.1}, {0,1,1,1} },
{ {-0.5, 0.5, -2.1}, {0,1,1,1} },
{ {0.5, 0.5, -2.1}, {0,1,1,1} },
{ {0.5, -0.5, -2.1}, {0,1,1,1} },
},
{
0,1,2, 2,3,0, 4,5,6, 6,7,4, 0,4,5, 1,5,0
},
true
}
};
float tx = 0;
float ty = 0;
float tz = 0;
while (!glfwWindowShouldClose(window.window) )
{
glfwMakeContextCurrent(window.window);
renderer.Clear({0.0, 0.0, 0.0, 1});
// feed inputs to dear imgui, start new frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
renderer.Draw(shader, window.mvp, cubes);
window.mvp.view = glm::translate(glm::mat4(1), glm::vec3(0, 0, -1.6)) * glm::rotate(glm::mat4(1), glm::radians(tx), glm::vec3(1,0,0)) * glm::rotate(glm::mat4(1), glm::radians(ty), glm::vec3(0,1,0)) * glm::rotate(glm::mat4(1), glm::radians(tz), glm::vec3(0,0,1));
window.mvp.view = glm::translate(window.mvp.view, glm::vec3(0, 0, 1.6));
renderer.Text(shader, MVP(glm::mat4(1.0), glm::mat4(1.0),glm::ortho(0.0, (double)width, 0.0, (double)height)), {(TextObj){"roboto", "FPS:" + std::to_string((int)ImGui::GetIO().Framerate), 0.01424501424f * height, 0.01424501424f * height, 0, 0.02849002849f * height, 0, {1,1,1,1}}});
// render your GUI
ImGui::Begin("Demo window");
ImGui::Button("Hello!");
ImGui::SliderFloat("x", &tx, 0, 360, "%.0f", 1.0f);
ImGui::SliderFloat("y", &ty, 0, 360, "%.0f", 1.0f);
ImGui::SliderFloat("z", &tz, 0, 360, "%.0f", 1.0f);
ImGui::End();
// Render dear imgui into screen
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
int display_w, display_h;
glfwGetFramebufferSize(window.window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glfwSwapBuffers(window.window);
glfwSwapInterval(1);
glfwPollEvents();
}
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
return 0;
}
and the output:
and heres the code with glm::ortho(-width / 2, width / 2, -height/2, height, 0.1f, 10.0f):
#include "Engine.h"
#include "vendor/stb/stb_image.h"
float width = 1200;
float height = 750;
// edits in imgui files have farouk comment before them
int main(void)
{
initGLFW();
GLFWimage icon;
icon.pixels = stbi_load("data/Images/Icon.png", &icon.width, &icon.height, nullptr, 4);
GLFWimage image;
image.pixels = stbi_load("data/Images/Cursor.png", &image.width, &image.height, nullptr, 4);
GLFWcursor * cursor = glfwCreateCursor(&image, image.width / 2, image.height / 2);
glfwWindowHint(GLFW_SAMPLES, 6);
glEnable(GL_MULTISAMPLE);
Window window("OGL", width, height, MVP());
Renderer renderer;
renderer.GenerateFonts({"data/fonts/arial.ttf", "data/fonts/Pixelboy.ttf"}, {"arial", "roboto"}, {64, 64});
// set perspective projection & set xyz ranges from -1, 1 to respective ranges
window.mvp.projection = glm::ortho(-width / 2, width / 2, -height/2, height, 0.1f, 10.0f);
window.createWindow("data/Images/Cursor.png", "data/Images/Icon.png");
glfwSetWindowIcon(window.window, 1, &icon);
glfwSetCursor(window.window, cursor);
stbi_image_free(image.pixels);
stbi_image_free(icon.pixels);
glfwMakeContextCurrent(window.window);
initGLEW(true);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
renderer.loadFonts({"arial", "roboto"});
initImGUI(window.window, "data/fonts/arial.ttf");
Shader shader("data/shaders/base.shader");
// TODO: Add Multiple Window Support (Window Manager)
// Addd .txt file for text with Characters map insteadt loaf Fonts
// texture in 3d space & 3d textures
// map -1, 1 to -width/2 , width/2
std::vector <Obj> cubes = {
{
{
{ {-300, -300, -1.1}, {1,1,1,1} },
{ {-300, 300, -1.1}, {1,1,1,1} },
{ {300, 300, -1.1}, {1,1,1,1} },
{ {300, -300, -1.1}, {1,1,1,1} },
{ {-300, -300, -2.1}, {0,1,1,1} },
{ {-300, 300, -2.1}, {0,1,1,1} },
{ {300, 300, -2.1}, {0,1,1,1} },
{ {300, -300, -2.1}, {0,1,1,1} },
},
{
0,1,2, 2,3,0, 4,5,6, 6,7,4, 0,4,5, 1,5,0
},
true
}
};
float tx = 0;
float ty = 0;
float tz = 0;
while (!glfwWindowShouldClose(window.window) )
{
glfwMakeContextCurrent(window.window);
renderer.Clear({0.0, 0.0, 0.0, 1});
// feed inputs to dear imgui, start new frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
renderer.Draw(shader, window.mvp, cubes);
window.mvp.view = glm::translate(glm::mat4(1), glm::vec3(0, 0, -1.6)) * glm::rotate(glm::mat4(1), glm::radians(tx), glm::vec3(1,0,0)) * glm::rotate(glm::mat4(1), glm::radians(ty), glm::vec3(0,1,0)) * glm::rotate(glm::mat4(1), glm::radians(tz), glm::vec3(0,0,1));
window.mvp.view = glm::translate(window.mvp.view, glm::vec3(0, 0, 1.6));
renderer.Text(shader, MVP(glm::mat4(1.0), glm::mat4(1.0),glm::ortho(0.0, (double)width, 0.0, (double)height)), {(TextObj){"roboto", "FPS:" + std::to_string((int)ImGui::GetIO().Framerate), 0.01424501424f * height, 0.01424501424f * height, 0, 0.02849002849f * height, 0, {1,1,1,1}}});
// render your GUI
ImGui::Begin("Demo window");
ImGui::Button("Hello!");
ImGui::SliderFloat("x", &tx, 0, 360, "%.0f", 1.0f);
ImGui::SliderFloat("y", &ty, 0, 360, "%.0f", 1.0f);
ImGui::SliderFloat("z", &tz, 0, 360, "%.0f", 1.0f);
ImGui::End();
// Render dear imgui into screen
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
int display_w, display_h;
glfwGetFramebufferSize(window.window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glfwSwapBuffers(window.window);
glfwSwapInterval(1);
glfwPollEvents();
}
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
return 0;
}
and the output:
notice that i chane the vertexes from 0.5 to 300 so thats not a problem
explanation of obj struct
{
// vertexes -> vbo
{ {-0.5, -0.5, -1.1}, {1,1,1,1} }, glm::vec3 pos, glm::vec4 col
{ {-0.5, 0.5, -1.1}, {1,1,1,1} },
{ {0.5, 0.5, -1.1}, {1,1,1,1} },
{ {0.5, -0.5, -1.1}, {1,1,1,1} },
{ {-0.5, -0.5, -2.1}, {0,1,1,1} },
{ {-0.5, 0.5, -2.1}, {0,1,1,1} },
{ {0.5, 0.5, -2.1}, {0,1,1,1} },
{ {0.5, -0.5, -2.1}, {0,1,1,1} },
},
{
// indexex -> ibo
0,1,2, 2,3,0, 4,5,6, 6,7,4, 0,4,5, 1,5,0
},
true // fill -> GL_TRIANGLES or GL_LINES
}
Thanks to #derhass for the clarification the cuboid that I was drawing was too thin and my Z far value was too short I've adjusted it to 1000.0f and now everything works great
here is his comment:
"The problem is that the cube becomes a line". There are no cubes in your vertex data, there is a cuboid sized 600x600x1, so if you look at it at certain angles, it will look very thin of course. And furthermore, you will run into clipping issues as your z range is only 10 units.
I'm trying to take a noise pattern which consists of black and white (and grey where there is a smooth transition between the two) and I am trying to map it to two different colours, but I'm having trouble figuring out how to do this.
I can easily replace the white or black with a simple if statement, but the gradient areas where the white and black is mixed is still a mix of white and black, which makes sense. So I need to actually the map the colours to the new colours, but I have no idea the way I'm supposed to go about this.
There are easy ways
The inflexible way, use mix
gl_FragColor = mix(color0, color1, noise)
The more flexible way, use a ramp texture
float u = (noise * (rampTextureWidth - 1.0) + 0.5) / rampTextureWidth;
gl_FragColor = texture2D(rampTexture, vec2(u, 0.5));
Using ramp textures handles any number of colors where as mix only handles 2.
const vs = `
attribute vec4 position;
attribute float noise;
uniform mat4 u_matrix;
varying float v_noise;
void main() {
gl_Position = u_matrix * position;
v_noise = noise;
}
`;
const fs = `
precision highp float;
varying float v_noise;
uniform sampler2D rampTexture;
uniform float rampTextureWidth;
void main() {
float u = (v_noise * (rampTextureWidth - 1.0) + 0.5) / rampTextureWidth;
gl_FragColor = texture2D(rampTexture, vec2(u, 0.5));
}
`;
"use strict";
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
// compiles shaders, links program, looks up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
/*
6------7
/| /|
/ | / |
2------3 |
| | | |
| 4---|--5
| / | /
|/ |/
0------1
*/
const arrays = {
position: [
-1, -1, -1,
1, -1, -1,
-1, 1, -1,
1, 1, -1,
-1, -1, 1,
1, -1, 1,
-1, 1, 1,
1, 1, 1,
],
noise: {
numComponents: 1,
data: [
1, 0.5, 0.2, 0.3, 0.9, 0.1, 0.7, 1,
],
},
indices: [
0, 2, 1, 1, 2, 3,
1, 3, 5, 5, 3, 7,
5, 7, 4, 4, 7, 6,
4, 6, 0, 0, 6, 2,
2, 6, 3, 6, 7, 3,
0, 1, 4, 4, 1, 5,
],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
const red = [255, 0, 0, 255];
const yellow = [255, 255, 0, 255];
const blue = [ 0, 0, 255, 255];
const green = [ 0, 255, 0, 255];
const cyan = [ 0, 255, 255, 255];
const magenta = [255, 0, 255, 255];
function makeTexture(gl, name, colors) {
const width = colors.length / 4;
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
width, 1, 0,
gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array(colors));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
return {
name,
texture,
width,
};
}
const textures = [
makeTexture(gl, 'one color',
[...red]),
makeTexture(gl, 'two colors',
[...red, ...yellow]),
makeTexture(gl, 'three colors',
[...blue, ...red, ...yellow]),
makeTexture(gl, 'six colors',
[...green, ...red, ...blue, ...yellow, ...cyan, ...magenta]),
];
const infoElem = document.querySelector('#info');
function render(time) {
time *= 0.001;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
// draw cube
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 40;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 4, -7];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
const world = m4.rotationY(time);
gl.useProgram(programInfo.program);
const tex = textures[time / 2 % textures.length | 0];
infoElem.textContent = tex.name;
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniformXXX, gl.activeTexture, gl.bindTexture
twgl.setUniformsAndBindTextures(programInfo, {
u_matrix: m4.multiply(viewProjection, world),
rampTexture: tex.texture,
rampTextureWidth: tex.width,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
<div id="info"></div>
I recently learn opengl es 2.0, and now I try to make a gaussian blur on triangles generate by myself. I have some difficult to understand examples on the web and most apply the blur on an image. I know I have to use framebuffer but I don't know how to draw triangle on this and apply blur.
Is it possible to see a real and complete code in C++ with good explication ?
EDIT :
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#define GLFW_INCLUDE_ES2
#include <GLFW/glfw3.h>
#include "shaders.hpp"
#include "camera.hpp"
unsigned int vbo, cbo, tbo;
GLuint _fbo, _fbo2, _tex, _tex2;
static const GLuint WIDTH = 800;
static const GLuint HEIGHT = 600;
GLuint pos, col, tex, normal;
camera * _camera = new camera();
static const GLfloat vertices[] = {
0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, -1.0f, 0.0f
};
static const GLfloat colors[] = {
0.0f, 0.5f, 1.0f,
0.5f, 0.5f, 1.0f,
0.5f, 0.5f, 1.0f
};
static const GLfloat texture[] = {
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 1.0f
};
int main(void){
GLFWwindow* window;
shaders * shaderBasic;
GLuint pId;
glm::mat4 projection; static glm::mat4 view; static glm::mat4 model;
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
window = glfwCreateWindow(WIDTH, HEIGHT, __FILE__, NULL, NULL);
glfwMakeContextCurrent(window);
printf("GL_VERSION : %s\n", glGetString(GL_VERSION) );
printf("GL_RENDERER : %s\n", glGetString(GL_RENDERER) );
std::string vs, fs;
vs = "basic.vs";
fs = "basic.fs";
shaderBasic = new shaders(vs, fs);
shaderBasic->CompileShader();
shaderBasic->LinkShader();
pId = shaderBasic->getProgramId();
pos = glGetAttribLocation(pId, "position");
col = glGetAttribLocation(pId, "colors");
tex = glGetAttribLocation(pId, "tex");
fs = "lastBlur.fs";
shaders * blurShader;
GLuint pIdBlur;
blurShader = new shaders(vs, fs);
blurShader->CompileShader();
blurShader->LinkShader();
pIdBlur = blurShader->getProgramId();
_camera->setPositionCamera(glm::vec3(0, 0, -1));
_camera->setLookAtCamera(glm::vec3(0, 0, 0));
_camera->setFieldOfView(45);
_camera->setAspect(WIDTH, HEIGHT);
_camera->setViewport(WIDTH, HEIGHT);
_camera->getMatricies(projection, view, model);
glGenFramebuffers(1, &_fbo);
glGenTextures(1, &_tex);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glBindTexture(GL_TEXTURE_2D, _tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH/2, HEIGHT/2, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _tex, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
else{
std::cout << "FRAMEBUFFER COMPLETE" << std::endl;
}
auto sampTex = glGetUniformLocation(pIdBlur, "texture0");
std::cerr << "sampTex : " << sampTex << std::endl;
glUniform1i(sampTex, 0);
while (!glfwWindowShouldClose(window)) {
// glViewport(0, 0, WIDTH, HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glClearColor(0.0f, 0.0f, 0.4f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// glViewport(0, 0, WIDTH/2, HEIGHT/2);
glUseProgram(pIdBlur);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(pos, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(pos);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &cbo);
glBindBuffer(GL_ARRAY_BUFFER, cbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glVertexAttribPointer(col, 2, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(col);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &tbo);
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(texture), texture, GL_STATIC_DRAW);
glVertexAttribPointer(tex, 2, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(tex);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(pId);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _tex);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwPollEvents();
glfwSwapBuffers(window);
}
glDeleteBuffers(1, &vbo);
glfwTerminate();
return EXIT_SUCCESS;
}
Blur Shader:
#version 100
precision mediump float;
uniform sampler2D texture0;
varying vec3 vColor;
varying vec2 TexCoords;
vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
vec4 color = vec4(0.0);
vec2 off1 = vec2(1.411764705882353) * direction;
vec2 off2 = vec2(3.2941176470588234) * direction;
vec2 off3 = vec2(5.176470588235294) * direction;
color += texture2D(image, uv) * 0.1964825501511404;
color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344;
color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344;
color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732;
color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732;
color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057;
color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057;
return color;
}
void main(){
gl_FragColor = blur13(texture0, TexCoords, vec2(400, 300), vec2(1.0, 0.0));
}
I assume you have swapped pIdBlur and pId.
I' will give you introductions for gaussian blur shader with 2 passes. This is an approximation which first blurs along the X-Axis in the 1st pass and along the Y-Axis in the 2nd pass. This results in a better performance for strong blurring.
The blur shader uses a normal (or Gaussian) distribution. For the 2 passes is used the same shader program, with individual direction settings for the 2 passes, stored in the uniform vec2 u_dir. The strength of the blur effect can be varied with the uniform variable float u_sigma in the range [0.0, 1.0].
Blur Vertex shader
precision mediump float;
attribute vec2 inPos;
varying vec2 pos;
void main()
{
pos = inPos;
gl_Position = vec4( inPos, 0.0, 1.0 );
}
Blur Fragment shader
precision mediump float;
varying vec2 pos;
uniform sampler2D u_texture;
uniform vec2 u_textureSize;
uniform float u_sigma;
uniform vec2 u_dir;
float CalcGauss( float x, float sigma )
{
if ( sigma <= 0.0 )
return 0.0;
return exp( -(x*x) / (2.0 * sigma) ) / (2.0 * 3.14157 * sigma);
}
void main()
{
vec2 texC = pos.st * 0.5 + 0.5;
vec4 texCol = texture2D( u_texture, texC );
vec4 gaussCol = vec4( texCol.rgb, 1.0 );
vec2 step = u_dir / u_textureSize;
for ( int i = 1; i <= 32; ++ i )
{
float weight = CalcGauss( float(i) / 32.0, u_sigma * 0.5 );
if ( weight < 1.0/255.0 )
break;
texCol = texture2D( u_texture, texC + step * float(i) );
gaussCol += vec4( texCol.rgb * weight, weight );
texCol = texture2D( u_texture, texC - step * float(i) );
gaussCol += vec4( texCol.rgb * weight, weight );
}
gaussCol.rgb = clamp( gaussCol.rgb / gaussCol.w, 0.0, 1.0 );
gl_FragColor = vec4( gaussCol.rgb, 1.0 );
}
After the program has been linked the uniform locations and attribute indices can be read from:
GLint attrInxPos = glGetAttribLocation( pIdBlur, "inPos" );
GLint locTexture = glGetUniformLocation( pIdBlur, "u_texture" );
GLint locTexSize = glGetUniformLocation( pIdBlur, "u_textureSize" );
GLint locSigma = glGetUniformLocation( pIdBlur, "u_sigma" );
GLint locDir = glGetUniformLocation( pIdBlur, "u_dir" );
A vertex array object, containing a quad, which later will be drawn over the whole viewport, for a screen space blur pass, has to be created:
GLuint screenVAO;
glGenVertexArrays( 1, &screenVAO );
glBindVertexArray( screenVAO );
GLuint quadBuf;
glGenBuffers( 1, &quadBuf );
glBindBuffer( GL_ARRAY_BUFFER, quadBuf );
GLfloat screenRect[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f };
glBufferData( GL_ARRAY_BUFFER, 8 * sizeof( float ), screenRect, GL_STATIC_DRAW );
glEnableVertexAttribArray( attrInxPos );
glVertexAttribPointer( attrInxPos, 2, GL_FLOAT, GL_FALSE, 0, nullptr );
2 frame buffers, with a texture attached to its color plane, have to be created. In the 1st one the scene is drawn. The
2nd one is used by the 1st blur pass. The 2nd blur pass draws directly to the drawing buffer.
GLuint texObj[2];
GLuint fbObj[2];
glGenTextures(2, texObj);
glGenFramebuffers(2, fbObj);
glActiveTexture(GL_TEXTURE0);
for ( int i = 0; i < 2; i ++ )
{
glBindTexture(GL_TEXTURE_2D, texObj[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindFramebuffer(GL_FRAMEBUFFER, fbObj[i]);
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texObj[i], 0 );
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer( GL_RENDERBUFFER, renderbuffer );
glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height );
glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer );
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Now everything what is needed for the blur passes has been generated.
To draw and blur the scene the following steps have to be applied.
First you have to bind and clear the 1st frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, fbObj[0]);
glClearColor(0.0f, 0.0f, 0.4f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
and use the shader program for drawing the objects:
glUseProgram(pId);
Now draw the object(s) of the scene.
.....
glDrawArrays(GL_TRIANGLES, 0, 3);
The second step is the 1st blur pass. The blur program has to be use and the 2nd framebuffer has to be bound.
After the frame 1st buffer has been released, you can use the texture, that is attached to its color plane, as an input for the blur shader. Note, a texture can't be source and destination at the same time, this would cause undefined behavior.
To bind the texture to the shader, you have to bind the texture to a texture unit and assign the index of the texture unit to the uniform sampler of the shader.
int texUnitIndex = 1;
GLfloat texSize = { width, height };
GLfloat dirX[] = { 1.0f, 0.0f };
GLfloat sigma = .....; // 0.0 <= sigma <= 1.0
glBindFramebuffer(GL_FRAMEBUFFER, fbObj[1]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(pIdBlur);
glActiveTexture(GL_TEXTURE0 + texUnitIndex);
glBindTexture(GL_TEXTURE_2D, texObj[0]);
glUniform1i(locTexture, texUnitIndex);
glUniform2fv(locTexSize, texSize);
glUniform2fv(locTexSize, dirX);
glUniform1f(locTexSize, sigma);
To apply the blur pass a quad has to be drawn of the viewport area.
glBindVertexArray( screenVAO );
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
The 2nd and final blur pass, is similar to the 1st blur pass. The target texture of the 1st blur pass is the source texture, and the target is the drawing buffer. The blur direction has to be set up for the Y axis of the viewport.
GLfloat dirY[] = { 0.0f, 1.0f };
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texObj[1]);
glUniform2fv(locTexSize, dirY);
See also the answers to the following question:
How to get a "Glow" shader effect in OpenGL ES 2.0?
What kind of blurs can be implemented in pixel shaders?
See alos a similar WebGL example:
(function loadscene() {
var resize, gl, progDraw, progBlur, vp_size, blurFB;
var canvas, camera, bufCube = {}, bufQuad = {};
var shininess = 10.0, glow = 10.0, sigma = 0.8, radius = 1.0;
function render(deltaMS){
var sliderScale = 100;
sigma = document.getElementById( "sigma" ).value / sliderScale;
radius = document.getElementById( "radius" ).value / sliderScale;
vp_size = [canvas.width, canvas.height];
camera.Update( vp_size );
gl.enable( gl.DEPTH_TEST );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
// set up framebuffer
gl.bindFramebuffer( gl.FRAMEBUFFER, blurFB[0] );
gl.viewport( 0, 0, blurFB[0].width, blurFB[0].height );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
// setup view projection and model
var prjMat = camera.Perspective();
var viewMat = camera.LookAt();
var modelMat = RotateAxis( IdentM44(), Fract( deltaMS / 13000.0 ) * 2.0 * Math.PI, 0 );
modelMat = RotateAxis( modelMat, Fract( deltaMS / 17000.0 ) * 2.0 * Math.PI, 1 );
// set up draw shader
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", prjMat );
ShProg.SetM44( progDraw, "u_modelViewMat44", Multiply(viewMat, modelMat) );
ShProg.SetF1( progDraw, "u_shininess", shininess );
// draw scene
VertexBuffer.Draw( bufCube );
// set blur-X framebuffer and bind frambuffer texture
gl.bindFramebuffer( gl.FRAMEBUFFER, blurFB[1] );
gl.viewport( 0, 0, blurFB[1].width, blurFB[1].height );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
var texUnit = 1;
gl.activeTexture( gl.TEXTURE0 + texUnit );
gl.bindTexture( gl.TEXTURE_2D, blurFB[0].color0_texture );
// set up blur-X shader
ShProg.Use( progBlur );
ShProg.SetI1( progBlur, "u_texture", texUnit )
ShProg.SetF2( progBlur, "u_textureSize", vp_size );
ShProg.SetF1( progBlur, "u_sigma", sigma )
ShProg.SetF1( progBlur, "u_radius", radius )
ShProg.SetF2( progBlur, "u_dir", [1.0, 0.0] )
// draw full screen space
gl.enableVertexAttribArray( progBlur.inPos );
gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos );
gl.vertexAttribPointer( progBlur.inPos, 2, gl.FLOAT, false, 0, 0 );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx );
gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
gl.disableVertexAttribArray( progBlur.inPos );
// reset framebuffer and bind frambuffer texture
gl.bindFramebuffer( gl.FRAMEBUFFER, null );
gl.viewport( 0, 0, vp_size[0], vp_size[1] );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
texUnit = 2;
gl.activeTexture( gl.TEXTURE0 + texUnit );
gl.bindTexture( gl.TEXTURE_2D, blurFB[1].color0_texture );
// set up pst process shader
ShProg.SetI1( progBlur, "u_texture", texUnit )
ShProg.SetF1( progBlur, "u_radius", radius )
ShProg.SetF2( progBlur, "u_dir", [0.0, 1.0] )
// draw full screen space
gl.enableVertexAttribArray( progBlur.inPos );
gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos );
gl.vertexAttribPointer( progBlur.inPos, 2, gl.FLOAT, false, 0, 0 );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx );
gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
gl.disableVertexAttribArray( progBlur.inPos );
requestAnimationFrame(render);
}
function initScene() {
canvas = document.getElementById( "canvas");
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
return null;
progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = gl.getAttribLocation( progDraw.progObj, "inPos" );
progDraw.inNV = gl.getAttribLocation( progDraw.progObj, "inNV" );
progDraw.inCol = gl.getAttribLocation( progDraw.progObj, "inCol" );
progBlur = ShProg.Create(
[ { source : "post-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "blur-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
progBlur.inPos = gl.getAttribLocation( progBlur.progObj, "inPos" );
if ( !progBlur.progObj )
return;
// create cube
var cubePos = [
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0 ];
var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ];
var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ];
var cubePosData = [];
for ( var i = 0; i < cubeHlpInx.length; ++ i ) {
cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] );
}
var cubeNVData = [];
for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) {
var nv = [0, 0, 0];
for ( i2 = 0; i2 < 4; ++ i2 ) {
var i = i1 + i2;
nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2];
}
for ( i2 = 0; i2 < 4; ++ i2 )
cubeNVData.push( nv[0], nv[1], nv[2] );
}
var cubeColData = [];
for ( var is = 0; is < 6; ++ is ) {
for ( var ip = 0; ip < 4; ++ ip ) {
cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] );
}
}
var cubeInxData = [];
for ( var i = 0; i < cubeHlpInx.length; i += 4 ) {
cubeInxData.push( i, i+1, i+2, i, i+2, i+3 );
}
bufCube = VertexBuffer.Create(
[ { data : cubePosData, attrSize : 3, attrLoc : progDraw.inPos },
{ data : cubeNVData, attrSize : 3, attrLoc : progDraw.inNV },
{ data : cubeColData, attrSize : 3, attrLoc : progDraw.inCol } ],
cubeInxData );
bufQuad.pos = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos );
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( [ -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0 ] ), gl.STATIC_DRAW );
bufQuad.inx = gl.createBuffer();
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx );
gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ), gl.STATIC_DRAW );
camera = new Camera( [0, 3, 0.0], [0, 0, 0], [0, 0, 1], 90, vp_size, 0.5, 100 );
window.onresize = resize;
resize();
requestAnimationFrame(render);
}
function resize() {
//vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
vp_size = [window.innerWidth, window.innerHeight]
//vp_size = [256, 256]
canvas.width = vp_size[0];
canvas.height = vp_size[1];
var fbsize = Math.max(vp_size[0], vp_size[1]);
fbsize = 1 << 31 - Math.clz32(fbsize); // nearest power of 2
blurFB = [];
for ( var i = 0; i < 2; ++ i ) {
fb = gl.createFramebuffer();
fb.width = fbsize;
fb.height = fbsize;
gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
fb.color0_texture = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, fb.color0_texture );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, fb.width, fb.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
fb.renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer( gl.RENDERBUFFER, fb.renderbuffer );
gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, fb.width, fb.height );
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fb.color0_texture, 0 );
gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fb.renderbuffer );
gl.bindTexture( gl.TEXTURE_2D, null );
gl.bindRenderbuffer( gl.RENDERBUFFER, null );
gl.bindFramebuffer( gl.FRAMEBUFFER, null );
blurFB.push( fb );
}
}
function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}
function IdentM44() {
return [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ];
};
function RotateAxis(matA, angRad, axis) {
var aMap = [ [1, 2], [2, 0], [0, 1] ];
var a0 = aMap[axis][0], a1 = aMap[axis][1];
var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
var matB = matA.slice(0);
for ( var i = 0; i < 3; ++ i ) {
matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
}
return matB;
}
function Rotate(matA, angRad, axis) {
var s = Math.sin(angRad), c = Math.cos(angRad);
var x = axis[0], y = axis[1], z = axis[2];
matB = [
x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s, 0,
y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s, 0,
z*x*(1-c)-y*s, z*y*(1-c)+x*s, z*z*(1-c)+c, 0,
0, 0, 0, 1 ];
return Multiply(matA, matB);
}
function Multiply(matA, matB) {
matC = IdentM44();
for (var i0=0; i0<4; ++i0 )
for (var i1=0; i1<4; ++i1 )
matC[i0*4+i1] = matB[i0*4+0] * matA[0*4+i1] + matB[i0*4+1] * matA[1*4+i1] + matB[i0*4+2] * matA[2*4+i1] + matB[i0*4+3] * matA[3*4+i1]
return matC;
}
function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; }
function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
function Normalize( v ) {
var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
return [ v[0] / len, v[1] / len, v[2] / len ];
}
Camera = function( pos, target, up, fov_y, vp, near, far ) {
this.Time = function() { return Date.now(); }
this.pos = pos;
this.target = target;
this.up = up;
this.fov_y = fov_y;
this.vp = vp;
this.near = near;
this.far = far;
this.Perspective = function() {
var n = this.near;
var f = this.far;
var fn = f + n;
var f_n = f - n;
var r = this.vp[0] / this.vp[1];
var t = 1 / Math.tan( Math.PI * this.fov_y / 360 );
return [
t/r, 0, 0, 0,
0, t, 0, 0,
0, 0, -fn/f_n, -1,
0, 0, -2*f*n/f_n, 0 ];
};
this.LookAt = function() {
var mz = Normalize( [ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ] );
var mx = Normalize( Cross( this.up, mz ) );
var my = Normalize( Cross( mz, mx ) );
var tx = Dot( mx, this.pos );
var ty = Dot( my, this.pos );
var tz = Dot( [-mz[0], -mz[1], -mz[2]], this.pos );
return [mx[0], my[0], mz[0], 0, mx[1], my[1], mz[1], 0, mx[2], my[2], mz[2], 0, tx, ty, tz, 1];
};
this.Update = function(vp_size) {
if (vp_size)
this.vp = vp_size;
};
}
var ShProg = {
Create: function (shaderList) {
var shaderObjs = [];
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
var noOfAttributes = gl.getProgramParameter(prog.progObj, gl.ACTIVE_ATTRIBUTES);
for (var i_n = 0; i_n < noOfAttributes; ++i_n) {
var name = gl.getActiveAttrib(prog.progObj, i_n).name;
prog.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
var noOfUniforms = gl.getProgramParameter(prog.progObj, gl.ACTIVE_UNIFORMS);
for (var i_n = 0; i_n < noOfUniforms; ++i_n) {
var name = gl.getActiveUniform(prog.progObj, i_n).name;
prog.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: function (source, shaderStage) {
var shaderScript = document.getElementById(source);
if (shaderScript)
source = shaderScript.text;
var shaderObj = gl.createShader(shaderStage);
gl.shaderSource(shaderObj, source);
gl.compileShader(shaderObj);
var status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
if (!status) alert(gl.getShaderInfoLog(shaderObj));
return status ? shaderObj : null;
},
Link: function (shaderObjs) {
var prog = gl.createProgram();
for (var i_sh = 0; i_sh < shaderObjs.length; ++i_sh)
gl.attachShader(prog, shaderObjs[i_sh]);
gl.linkProgram(prog);
status = gl.getProgramParameter(prog, gl.LINK_STATUS);
if ( !status ) alert(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };
var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: [], attr: [], inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
Draw: function(bufObj) {
for (var i=0; i<bufObj.buf.length; ++i) {
gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.buf[i]);
gl.vertexAttribPointer(bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray( bufObj.attr[i].loc);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };
initScene();
})();
html,body { margin: 0; overflow: hidden; }
#gui { position : absolute; top : 0; left : 0; }
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec3 inPos;
attribute vec3 inNV;
attribute vec3 inCol;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
uniform mat4 u_projectionMat44;
uniform mat4 u_modelViewMat44;
void main()
{
vertNV = mat3( u_modelViewMat44 ) * normalize( inNV );
vertCol = inCol;
vec4 pos = u_modelViewMat44 * vec4( inPos, 1.0 );
vertPos = pos.xyz / pos.w;
gl_Position = u_projectionMat44 * pos;
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
uniform float u_shininess;
void main()
{
vec3 color = vertCol;
vec3 normalV = normalize( vertNV );
vec3 eyeV = normalize( -vertPos );
vec3 halfV = normalize( eyeV + normalV );
float NdotH = max( 0.0, dot( normalV, halfV ) );
float shineFac = ( u_shininess + 2.0 ) * pow( NdotH, u_shininess ) / ( 2.0 * 3.14159265 );
gl_FragColor = vec4( color.rgb * (0.2 + NdotH), 1.0 );
}
</script>
<script id="post-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 pos;
void main()
{
pos = inPos;
gl_Position = vec4( inPos, 0.0, 1.0 );
}
</script>
<script id="blur-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 pos;
uniform sampler2D u_texture;
uniform vec2 u_textureSize;
uniform float u_sigma;
uniform float u_radius;
uniform vec2 u_dir;
float CalcGauss( float x, float sigma )
{
if ( sigma <= 0.0 )
return 0.0;
return exp( -(x*x) / (2.0 * sigma) ) / (2.0 * 3.14157 * sigma);
}
void main()
{
vec2 texC = pos.st * 0.5 + 0.5;
vec4 texCol = texture2D( u_texture, texC );
vec4 gaussCol = vec4( texCol.rgb, 1.0 );
vec2 step = u_dir / u_textureSize;
for ( int i = 1; i <= 32; ++ i )
{
float weight = CalcGauss( float(i) / 32.0, u_sigma * 0.5 );
if ( weight < 1.0/255.0 )
break;
texCol = texture2D( u_texture, texC + u_radius * step * float(i) );
gaussCol += vec4( texCol.rgb * weight, weight );
texCol = texture2D( u_texture, texC - u_radius * step * float(i) );
gaussCol += vec4( texCol.rgb * weight, weight );
}
gaussCol.rgb = clamp( gaussCol.rgb / gaussCol.w, 0.0, 1.0 );
gl_FragColor = vec4( gaussCol.rgb, 1.0 );
}
</script>
<div>
<form id="gui" name="inputs">
<table>
<tr> <td> <font color= #CCF>radius</font> </td>
<td> <input type="range" id="radius" min="1" max="1000" value="200"/></td> </tr>
<tr> <td> <font color= #CCF>blur</font> </td>
<td> <input type="range" id="sigma" min="1" max="50" value="10"/></td> </tr>
</table>
</form>
</div>
<canvas id="canvas" style="border: none;"></canvas>
In general you need to draw the scene you want to blur to a frame buffer object (FBO) with attached texture.
Create a frame buffer
Create an empty texture (data parameter should be null)
Bind frame buffer and texture
Attach the texture to frame buffer as color
At this point the rest of the drawing should be exactly the same as on your main buffer but make sure you setup a correct viewport. This procedure will make you draw to the texture.
Now that you have a texture with your scene you need to go to the same procedure as is for blurring an image.
Bind your main buffer (usually indexed 0)
Bind texture
Draw the texture to main buffer with blur shader
You can then optimize it with horizontal and vertical blur shaders having 2 calls which uses yet another FBO...
So I would try doing some steps in your application:
Create a scene which draws and shows a triangle
Create a FBO, draw to it and draw the FBO texture on your main buffer
Create a scene which draws and shows a blurred image through texture
Create a FBO on which the scene is drawn then draws a blurred texture from FBO on the main buffer
If you find yourself in trouble at any of these points you might want to ask a specific question about it.
With WebGL 2 we now can play with Uniform Buffer Objects.
They look like a great idea, not having to attach common uniforms to every single program (like projection and view matrices that are common to every object being rendered).
I created an helper class which I call every time I want to bind a uniform buffer object.
class UniformBuffer {
constructor(gl, data, boundLocation = 0) {
this.boundLocation = boundLocation;
this.data = new Float32Array(data);
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
};
The idea if to create the uniform buffers like this
const perScene = new UniformBuffer(gl, [
...vec4.create(),
...vec4.create(),
], 0); // and bind it to bind location 0?
const perObject = new UniformBuffer(gl, [
...vec4.create(),
], 1); // and bind it to bind location 1?
In my render loop, I then update the "perScene" uniforms by calling
perScene.update(gl, [
...vec4.fromValues(1, 0, 0, 1),
], 4); // giving an offset to update only the 2nd color.
Then I'll look through all the objects in the scene and my idea is to update the perObject uniform buffer like this
for (let i = 0; i < objects.length; i++) {
perObject.update(gl, [
...vec4.fromValues(0, 0, 1, 1),
]);
}
I'm talking about vec4 just to make the example easier, but the idea is to have matrices (projection and view) on the perScene, and (object and normal matrices) on the perObject.
In my shader I have them declared as
uniform perScene {
vec4 color1;
vec4 color2;
};
uniform perModel {
vec4 color3;
};
I have a working snippet here
class UniformBuffer {
constructor(gl, data, boundLocation = 0) {
this.boundLocation = boundLocation;
this.data = new Float32Array(data);
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
};
const vertex = `#version 300 es
uniform perScene {
vec4 color1;
vec4 color2;
};
uniform perModel {
vec4 color3;
};
in vec3 a_position;
out vec3 v_color;
void main() {
gl_Position = vec4(a_position, 1.0);
v_color = color1.rgb + color2.rgb; // WORKS
// v_color = color1.rgb + color2.rgb + color3.rgb; // DOESNT WORK
}
`;
const fragment = `#version 300 es
precision highp float;
precision highp int;
in vec3 v_color;
out vec4 outColor;
void main() {
outColor = vec4(v_color, 1.0);
}
`;
const geometry = {
positions: [-0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0],
indices: [0, 2, 1, 1, 2, 3],
};
const renderList = [];
// STEP 1 (create canvas)
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl2");
if (!gl) {
console.log('no webgl2 buddy');
}
// STEP 2 (create program)
const v = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(v, vertex);
gl.compileShader(v);
const f = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(f, fragment);
gl.compileShader(f);
const program = gl.createProgram();
gl.attachShader(program, v);
gl.attachShader(program, f);
gl.linkProgram(program);
// STEP 3 (create VAO)
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const colorUniformLocation = gl.getUniformLocation(program, 'color');
const positionsBuffer = gl.createBuffer();
const indicesBuffer = gl.createBuffer();
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
// position & indices
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(geometry.positions), gl.STATIC_DRAW);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(geometry.indices), gl.STATIC_DRAW);
// STEP 4 (create UBO)
// bound to location 0
const perScene = new UniformBuffer(gl, [
...vec4.create(), // color 1
...vec4.create(), // color 2
], 0);
// bound to location 1 ?
const perModel = new UniformBuffer(gl, [
...vec4.create(), // color 3
], 3);
// STEP 5 (add instances)
for (let i = 0; i < 1; i++) {
renderList.push({
id: i,
vao: vao,
program: program,
color: [0, 1, 1],
});
}
// STEP 6 (draw)
gl.clearColor(0, 0, 0, 0);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, canvas.width, canvas.height);
perScene.update(gl, [
...vec4.fromValues(1, 0, 0, 1),
...vec4.fromValues(0, 1, 0, 1),
]);
for (let i = 0; i < renderList.length; i++) {
const current = renderList[i];
gl.useProgram(current.program);
gl.bindVertexArray(current.vao);
// update perObject
perModel.update(gl, [
...vec4.fromValues(0, 0, 1, 1),
]);
gl.drawElements(gl.TRIANGLES, geometry.indices.length, gl.UNSIGNED_SHORT, 0);
// unbind
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
console.log('compiled!');
canvas {
background-color: black;
}
<canvas id="canvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
Shouldn't I be seeing a white square since all colours added up result in a vec4(1.0, 1.0, 1.0, 1.0)? (jsfiddle line 41)
What am I doing wrong?
Thanks
So, the first thing you're doing wrong is you're not calling gl.getUniformBlockIndex. Just like uniform you have to query the location or in this case the index of each block.
The second thing is block uniforms are indirected one level and you need to call gl.uniformBlockBinding(program, uniformBlockIndex, uniformBufferIndex);
uniformBlockIndex is the index you got from gl.getUniformBlockIndex. uniformBufferIndex similar to a texture unit. There are N uniform buffer indices. You can choose any buffer index from 0 to MAX_UNIFORM_BUFFER_BINDINGS - 1.
This indirection helps if you have one program that uses blocks A, B and another that uses A and C. In this case block A might have a different index in the 2 programs but you have it pull its values from the same uniformBufferIndex.
Note that this state is per program state so can probably set it at init time if you plan to always use the same uniform buffer index for the same uniform block.
To spell it out even more. You have a shader program. It has state
var someProgram = {
uniforms: {
projectionMatrix: [1, 0, 0, 0, 0, ... ], // etc
},
uniformBlockIndcies[ // one per uniform block
0,
0,
0,
],
...
}
Next you have uniform buffer indices which are global state
glState = {
textureUnits: [ ... ],
uniformBuffers: [ null, null, null ..., ],
};
You tell the program for each uniform buffer block, which uniform buffer index to use with gl.uniformBlockBinding. You then bind a buffer to that index with gl.bindBufferBase or gl.bindBufferRange.
It's very similar to telling a program which texture unit to use and then binding a texture to that unit. When you do this, at init time or render time is really up to you. In my mind it seems more likely I could decide at init time that my perScene stuff is always on buffer index 0 and perModel stuff at index 1 and therefore I could set them up the program parts (the calls to gl.uniformBlockBinding) at init time.
class UniformBuffer {
constructor(gl, data, boundLocation = 0) {
this.boundLocation = boundLocation;
this.data = new Float32Array(data);
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
};
const vertex = `#version 300 es
uniform perScene {
vec4 color1;
vec4 color2;
};
uniform perModel {
vec4 color3;
};
in vec3 a_position;
out vec3 v_color;
void main() {
gl_Position = vec4(a_position, 1.0);
v_color = color1.rgb + color2.rgb + color3.rgb;
}
`;
const fragment = `#version 300 es
precision highp float;
precision highp int;
in vec3 v_color;
out vec4 outColor;
void main() {
outColor = vec4(v_color, 1.0);
}
`;
const geometry = {
positions: [-0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0],
indices: [0, 2, 1, 1, 2, 3],
};
const renderList = [];
// STEP 1 (create canvas)
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl2");
if (!gl) {
console.log('no webgl2 buddy');
}
// STEP 2 (create program)
const v = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(v, vertex);
gl.compileShader(v);
const f = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(f, fragment);
gl.compileShader(f);
const program = gl.createProgram();
gl.attachShader(program, v);
gl.attachShader(program, f);
gl.linkProgram(program);
// STEP 3 (create VAO)
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const colorUniformLocation = gl.getUniformLocation(program, 'color');
const positionsBuffer = gl.createBuffer();
const indicesBuffer = gl.createBuffer();
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
// position & indices
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(geometry.positions), gl.STATIC_DRAW);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(geometry.indices), gl.STATIC_DRAW);
// STEP 4 (create UBO)
// bound to location 0
const perScene = new UniformBuffer(gl, [
...vec4.create(), // color 1
...vec4.create(), // color 2
], 0);
// bound to location 1 ?
const perModel = new UniformBuffer(gl, [
...vec4.create(), // color 3
], 1);
gl.uniformBlockBinding(program, gl.getUniformBlockIndex(program, "perScene"), perScene.boundLocation);
gl.uniformBlockBinding(program, gl.getUniformBlockIndex(program, "perModel"), perModel.boundLocation);
// STEP 5 (add instances)
for (let i = 0; i < 1; i++) {
renderList.push({
id: i,
vao: vao,
program: program,
color: [0, 1, 1],
});
}
// STEP 6 (draw)
gl.clearColor(0, 0, 0, 0);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, canvas.width, canvas.height);
perScene.update(gl, [
...vec4.fromValues(1, 0, 0, 1),
...vec4.fromValues(0, 1, 0, 1),
]);
for (let i = 0; i < renderList.length; i++) {
const current = renderList[i];
gl.useProgram(current.program);
gl.bindVertexArray(current.vao);
// update perObject
perModel.update(gl, [
...vec4.fromValues(0, 0, 1, 1),
]);
gl.drawElements(gl.TRIANGLES, geometry.indices.length, gl.UNSIGNED_SHORT, 0);
// unbind
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
console.log('compiled!');
canvas {
background-color: black;
}
<canvas id="canvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
In this example there are 5 uniform blocks.
the shared matrices like projection and view and viewProjection
the per model matrices like world and worldInverseTransform
the per light info like lightPosition and lightColor.
There are 2 lights so the 4th block is similar to the 3rd
the material data like ambient color, specularity, etc..
I'm not saying that's the perfect setup. I really have no idea. But it's pretty common to make something called a "material" and share that material among more than one model so that's like a perMaterial block which is different from a perModel block. It's also common to share lighting info. I don't know what the ideal setup is, just pointing out that perScene and perModel might not be enough for fairly common situations.
One other thing, this line
// unbind
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
makes no sense. ELEMENT_ARRAY_BUFFER is part of the VAO state.
As gman said, get the index of the uniform block and then bind it with the gl.bindBufferBase
Your updated class should look something like:
class UniformBuffer {
constructor(gl, data, program, uniformName, targetIndex = 0) {
this.data = new Float32Array(data);
const boundLocation = gl.getUniformBlockIndex(program, uniformName);
this.buffer = gl.createBuffer();
gl.bindBufferBase(gl.UNIFORM_BUFFER, boundLocation, this.buffer);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
}
};
Here is the demo image:
(Left top 256x256 rect is the depth texture)
I render the shadow map in the first pass(with parallel projection),
then render the scene in the second pass,
then render the scene with shadow map in the final pass.
the shadow is sometimes rendered twice or on the wrong surfaces
Is there any solution?
Full code here:
typedef struct
{
vec3_t org;//origin
vec3_t off;//position offset
vec3_t ang;//angle
float dist;//radius
int w;//default 512
int h;//default 512
int depth;//depth texture
int color;//color texture
int dimension;//default = 8
float mvmatrix[16];
float projmatrix[16];
cl_entity_t *followent;
int inuse;
}sdlight_t;
void R_RenderDepthMap()
{
qglPolygonOffset( 5.0, 0.0 );
qglEnable(GL_POLYGON_OFFSET_FILL);
if(cursdlight->followent)
{
VectorCopy(cursdlight->followent->origin, cursdlight->org);
}
qglMatrixMode(GL_PROJECTION);
qglLoadIdentity();
qglOrtho(-cursdlight->w / cursdlight->dimension, cursdlight->w / cursdlight->dimension, -cursdlight->h / cursdlight->dimension, cursdlight->h / cursdlight->dimension, -9999, 9999);//cursdlight->dist
qglMatrixMode(GL_MODELVIEW);
qglLoadIdentity();
qglRotatef(-90, 1, 0, 0);
qglRotatef(90, 0, 0, 1);
qglRotatef(-cursdlight->ang[2], 1, 0, 0);
qglRotatef(-cursdlight->ang[0], 0, 1, 0);
qglRotatef(-cursdlight->ang[1], 0, 0, 1);
qglTranslatef(-cursdlight->org[0], -cursdlight->org[1], -cursdlight->org[2]);
qglViewport(0, 0, cursdlight->w, cursdlight->h);
glGetFloatv(GL_PROJECTION_MATRIX, cursdlight->projmatrix);
glGetFloatv(GL_MODELVIEW_MATRIX, cursdlight->mvmatrix);
qglDepthRange(1.0, 0.0);
qglDepthFunc(GL_LEQUAL);
qglEnable(GL_CULL_FACE);
//qglCullFace(GL_FRONT);
qglClearColor(1, 1, 1, 1);
qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Render Models..
R_DrawEntitiesOnList();
qglDisable(GL_POLYGON_OFFSET_FILL);
qglBindFramebufferEXT(GL_READ_FRAMEBUFFER, s_BackBufferFBO.s_hBackBufferFBO);
qglActiveTextureARB(GL_TEXTURE0);
qglBindTexture(GL_TEXTURE_2D, cursdlight->depth);
qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 0, 0, cursdlight->w, cursdlight->h, 0);
}
void R_SetupShadowLight(void)
{
// enable automatic texture coordinates generation
GLfloat planeS[] = {1.0, 0.0, 0.0, 0.0};
GLfloat planeT[] = {0.0, 1.0, 0.0, 0.0};
GLfloat planeR[] = {0.0, 0.0, 1.0, 0.0};
GLfloat planeQ[] = {0.0, 0.0, 0.0, 1.0};
// setup texture stages
qglActiveTextureARB(GL_TEXTURE0_ARB);
qglBindTexture(GL_TEXTURE_2D, cursdlight->depth);
qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_REF_TO_TEXTURE);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
qglTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);
qglEnable(GL_TEXTURE_GEN_S);
qglEnable(GL_TEXTURE_GEN_T);
qglEnable(GL_TEXTURE_GEN_R);
qglEnable(GL_TEXTURE_GEN_Q);
qglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_S, GL_EYE_PLANE, planeS);
qglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_T, GL_EYE_PLANE, planeT);
qglTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_R, GL_EYE_PLANE, planeR);
qglTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
qglTexGenfv(GL_Q, GL_EYE_PLANE, planeQ);
// load texture projection matrix
qglMatrixMode(GL_TEXTURE);
qglLoadIdentity();
qglTranslatef(0.5, 0.5, 0.5);
qglScalef(0.5, 0.5, 0.5);
qglMultMatrixf(cursdlight->projmatrix);
qglMultMatrixf(cursdlight->mvmatrix);
qglMatrixMode(GL_MODELVIEW);
if (gl_polyoffset->value)
{
qglEnable(GL_POLYGON_OFFSET_FILL);
qglPolygonOffset(-1, -gl_polyoffset->value);
}
qglDepthMask(GL_FALSE);
qglEnable(GL_BLEND);
qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
qglColor4f(1,1,1,1);
qglUseProgramObjectARB(shadow_program);
qglUniform1iARB(shadow_uniform.shadowmap, 0);
}
void R_FinishShadowLight(void)
{
qglUseProgramObjectARB(0);
if (gl_polyoffset->value)
{
qglDisable(GL_POLYGON_OFFSET_FILL);
}
qglDepthMask(GL_TRUE);
qglDisable(GL_BLEND);
qglActiveTextureARB(GL_TEXTURE0_ARB);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
qglMatrixMode(GL_TEXTURE);
qglLoadIdentity();
qglDisable(GL_TEXTURE_GEN_S);
qglDisable(GL_TEXTURE_GEN_T);
qglDisable(GL_TEXTURE_GEN_R);
qglDisable(GL_TEXTURE_GEN_Q);
qglMatrixMode(GL_MODELVIEW);
}
void R_DrawShadows(void)
{
for (int i = 0; i < numsdlights; i++)
{
cursdlight = &sdlights[i];
if(!R_ShouldCastShadow())
continue;
R_SetupShadowLight();
R_DrawSceneShadow();
R_FinishShadowLight();
}
}
GLSL part:
//vertex shader
varying vec4 shadowcoord;
void main()
{
shadowcoord = gl_TextureMatrix[0] * gl_Vertex;
gl_Position = ftransform();
}
//fragment shader
#version 120
uniform sampler2DShadow shadowmap;
varying vec4 shadowcoord;
uniform float xoffset = 1.0/512.0;
uniform float yoffset = 1.0/512.0;
float lookup(vec4 coord, vec2 offSet)
{
return shadow2DProj(shadowmap, coord + vec4(offSet.x * xoffset, offSet.y * yoffset, 0.0, 0.0) ).w;
}
void main()
{
float shadow;
shadow = lookup(shadowcoord, vec2(0.0,0.0)) + lookup(shadowcoord, vec2(0.035,0.0)) + lookup(shadowcoord, vec2(-0.035,0.0)) + lookup(shadowcoord, vec2(0.0,0.035)) + lookup(shadowcoord, vec2(0.0,-0.035));
shadow /= 5.0;
if(shadow == 1.0)
discard;
else
gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-shadow) * 0.5);
}