In Blender Cycles render, there's a shader type called Glossy. Now I want to implement this glossy shader using OpenGL ES 2.0 on Android. I'm wondering where can I find a GLSL implementation of this shader? Below is an image displaying the glossy reflection (see the reflection on the plane).
That effect is basically just a gaussian blur filter. There is a good example of this for OpenGL ES 2.0 in the PowerVR SDK in these files:
\Examples\Intermediate\Bloom\OGLES2\BlurVertShader.vsh:
// Blur filter kernel shader
//
// 0 1 2 3 4
// x--x--X--x--x <- original filter kernel
// y---X---y <- filter kernel abusing the hardware texture filtering
// |
// texel center
//
//
// Using hardware texture filtering, the amount of samples can be
// reduced to three. To calculate the offset, use this formula:
// d = w1 / (w1 + w2), whereas w1 and w2 denote the filter kernel weights
attribute highp vec3 inVertex;
attribute mediump vec2 inTexCoord;
uniform mediump float TexelOffsetX;
uniform mediump float TexelOffsetY;
varying mediump vec2 TexCoord0;
varying mediump vec2 TexCoord1;
varying mediump vec2 TexCoord2;
void main()
{
// Pass through vertex
gl_Position = vec4(inVertex, 1.0);
// Calculate texture offsets and pass through
mediump vec2 offset = vec2(TexelOffsetX, TexelOffsetY);
TexCoord0 = inTexCoord - offset;
TexCoord1 = inTexCoord;
TexCoord2 = inTexCoord + offset;
}
\Examples\Intermediate\Bloom\OGLES2\BlurFragShader.vsh:
uniform lowp sampler2D sTexture;
/*
Separated Gaussian 5x5 filter, first row: 1 5 6 5 1
*/
varying mediump vec2 TexCoord0;
varying mediump vec2 TexCoord1;
varying mediump vec2 TexCoord2;
void main()
{
lowp vec3 color = texture2D(sTexture, TexCoord0).rgb * 0.333333;
color = color + texture2D(sTexture, TexCoord1).rgb * 0.333333;
color = color + texture2D(sTexture, TexCoord2).rgb * 0.333333;
gl_FragColor.rgb = color;
}
Related
Trying to implement refraction in OpenGL ES 2.0/3.0. Used the following shaders:
Vertex shader:
#version 300 es
precision lowp float;
uniform mat4 u_mvMatrix;
in vec4 a_position;
in vec3 a_normal;
...
out mediump vec2 v_refractCoord;
const mediump float eta = 0.95;
void main() {
vec4 eyePositionModel = u_mvMatrix * a_position;
// eye direction in model space
mediump vec3 eyeDirectModel = normalize(a_position.xyz - eyePositionModel.xyz);
// calculate refraction direction in model space
mediump vec3 refractDirect = refract(eyeDirectModel, a_normal, eta);
// project refraction
refractDirect = (u_mvpMatrix * vec4(refractDirect, 0.0)).xyw;
// map refraction direction to 2d coordinates
v_refractCoord = 0.5 * (refractDirect.xy / refractDirect.z) + 0.5;
...
}
Fragment shader:
...
in mediump vec2 v_refractCoord;
uniform samplerCube s_texture; // skybox
void main() {
outColor = texture(s_texture, vec3(v_refractCoord, 1.0));
}
Method for loading texture:
#JvmStatic
fun createTextureCubemap(context: Context, rowID: Int) {
val input = context.resources.openRawResource(rowID)
val bitmap = BitmapFactory.decodeStream(input)
val textureId = IntArray(1)
glGenTextures(1, textureId, 0)
glBindTexture(GL_TEXTURE_CUBE_MAP, textureId[0])
GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, bitmap, 0)
GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, bitmap, 0)
GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, bitmap, 0)
GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, bitmap, 0)
GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, bitmap, 0)
GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, bitmap, 0)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
return textureId[0]
}
But the texture is obtained with large pixels like:
What could be the reason for this? Maybe this is normal for a low-poly model? It seems that the texture is too close.
Note: The fewer polygons - the less quality becomes.
Thanks in advance for any comment/answer!
image from goodfon.ru
Solution: On the #Rabbid76 advice, I changed the normal data. It turned out that in the Blender you need to set the Shading for the object as smooth (no flat) - this increases the number of normals when exporting to the format *.obj: Why OBJ export writes face normals instead of vertex normals
Also, on the #Rabbid76 advice, I changed the line:
vec3 eyeDirectModel = normalize(- eyePositionModel.xyz);
As a result, pixelation has disappeared:
In addition, pixel artifacts may also appear when calculate refraction in the vertex shader, so I transferred the calculations to the fragment shader. Here is the modified shader code:
Vertex shader:
#version 300 es
precision lowp float;
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;
in vec4 a_position;
in vec3 a_normal;
out vec3 v_normal;
out lowp float SpecularIntensity;
out vec3 v_eyeDirectModel;
float getSpecularIntensity(vec4 position, vec3 a_normal, vec3 eyeDirectModel) {
float shininess = 30.0;
vec3 lightPosition = vec3(-20.0, 0.0, 0.0);
mediump vec3 LightDirModel = normalize(lightPosition - position.xyz);
mediump vec3 halfVector = normalize(LightDirModel + eyeDirectModel);
lowp float NdotH = max(dot(a_normal, halfVector), 0.0);
return pow(NdotH, shininess);
}
void main() {
v_normal = a_normal;
vec4 eyePositionModel = u_mvMatrix * a_position;
// Eye direction in model space
vec3 eyeDirectModel = normalize(- eyePositionModel.xyz);
// specular lighting
SpecularIntensity = getSpecularIntensity(a_position, a_normal, eyeDirectModel);
v_eyeDirectModel = eyeDirectModel;
gl_Position = u_mvpMatrix * a_position;
}
Fragment shader:
#version 300 es
precision lowp float;
uniform mat4 u_mvpMatrix;
in vec3 v_normal;
in lowp float SpecularIntensity;
in vec3 v_eyeDirectModel;
out vec4 outColor;
uniform samplerCube s_texture; // skybox
const float eta = 0.65;
void main() {
// Calculate refraction direction in model space
vec3 refractDirect = refract(v_eyeDirectModel, normalize(v_normal), eta);
// Project refraction
refractDirect = (u_mvpMatrix * vec4(refractDirect, 0.0)).xyw;
// Map refraction direction to 2d coordinates
vec2 refractCoord = 0.5 * (refractDirect.xy / refractDirect.z) + 0.5;
vec4 glassColor = texture(s_texture, vec3(refractCoord, 1.0));
outColor = glassColor + SpecularIntensity;
outColor.a = 0.8; // transparent
}
First of all there is a mistake in the shader code. a_position.xyz - eyePositionModel.xyz does not make any sense, since a_position is the vertex coordinate in model space and eyePositionModel is the vertex coordinate in view space.
You have to compute the incident vector for refract in view sapce. That is the vector from the eye position to the vertex. Since the eye position in view space is (0, 0, 0), it is:
vec4 eyePositionView = u_mvMatrix * a_position;
// eye direction in model space
mediump vec3 eyeDirectView = normalize(- eyePositionView.xyz);
Furthermore, it is an issue of the normal vector attributes.
The problem is caused by the fact that the normal vectors are computed per face rather than individually for each vertex.
Note, the refraction direction (refractDirect) depends on the vertex coordinate (eyeDirectModel) and the normal vector (a_normal):
mediump vec3 refractDirect = refract(eyeDirectModel, a_normal, eta);
Since the normal vectors are different between adjacent surfaces, you can see a noticeable edge between the faces of the the mesh.
If the normal vectors are computed per vertex, then the adjacent faces share the vertex coordinates and the corresponding normal vectors. That would causes a smooth transition from face to face.
I am currently programming an application for image processing. To achieve the needed performance, I have to use the GPU to compute the camera input, more specifically use OpenGL ES 2.0.
With the help of this project (https://github.com/yulu/ShaderCam) I achieved to pass the image to the pipeline and do simple operations with the fragment shader (like inverting colors etc).
My knowledge of GLSL, fragment shaders and vertex shaders is fairly limited but I am aware of pipeline constraints and what the two shaders do in the pipeline.
So - formulating the problem - I would like to calculate the average color of a row in my received image and return it (per row) to my application.
I read here https://stackoverflow.com/a/13866636/8038866 that this is generally possible, however I can't seem to find out the following things:
1 (edit: SOLVED by simply passing the w and h of my texture to the vertex and fragment shader):
Knowing where the row ends (and having that information in the fragment shader). For this I assume that I would have to pass the width of the picture to the vertex shader and from there to the fragment shader, right?
2.: How to calculate the average the color values of each row in the fragment shader and then pass them to the application. If I understand it correctly - the fragment shader only excutes the code per pixel, so I am not sure how to achieve this.
Here are the two very basic shaders
vertex shader:
uniform mat4 uTransformM;
uniform mat4 uOrientationM;
uniform vec2 ratios;
attribute vec2 aPosition;
varying vec2 vTextureCoord;
void main(){
gl_Position = vec4(aPosition, 0.0, 1.0);
vTextureCoord = (uTransformM * ((uOrientationM * gl_Position + 1.0)*0.5)).xy;
gl_Position.xy *= ratios;
}
fragment shader:
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES sTexture;
varying vec2 vTextureCoord;
void main(){
gl_FragColor = texture2D(sTexture, vTextureCoord);
//calc mean per row and pass it back
}
I am very thankful for every advice or help you can provide.
I found a way that does the trick for me. The idea is to calculate the mean for only one row of pixels and then later in the application to get this line with
glReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * data);
Here is my fragment shader (notice that the width of the surface is required as well):
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES sTexture;
varying float width;
varying vec2 vTextureCoord;
void main(){
vec4 accumulatedRGB = texture2D(sTexture, vec2(0,vTextureCoord.y));
if(vTextureCoord.x < 0.50 && vTextureCoord.x > 0.499){ //small enough to only cover one line
for(float i=1.0;i<=width;++i)
{
float xPosOnTexture = i/width;
vec4 current = texture2D(sTexture, vec2(xPosOnTexture,vTextureCoord.y));
accumulatedRGB += current;
}
vec4 mean = accumulatedRGB/width;
gl_FragColor = vec4(mean.rgb , mean.a);//avg color for one line
}
else{
gl_FragColor = vec4(0.0,0.0,0.0,0.0);//rest of the screen
}
}
I am writing an android game using Love2D in which I am using a Gaussian Blur Shader to get a bloom effect, however I am testing it on PC, and the results on PC and Android are very different, to the point of making the shader useless on Android.
Here is how the PC version looks like:
And Here is how the Android version looks like:
Both are running with the following shaders, doing a pass with offset direction vertical and horizontal.
// Vertex Shader:
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif
varying vec2 coordinate2b;
varying vec2 coordinate1b;
varying vec2 coordinate0f;
varying vec2 coordinate1f;
varying vec2 coordinate2f;
uniform vec2 offset_direction;
vec4 position(mat4 transform_projection, vec4 vertex_position)
{
coordinate2b = VertexTexCoord.xy + -3.230769 * offset_direction;
coordinate1b = VertexTexCoord.xy + -1.384615 * offset_direction;
coordinate0f = VertexTexCoord.xy + 0.000000 * offset_direction;
coordinate1f = VertexTexCoord.xy + 1.384615 * offset_direction;
coordinate2f = VertexTexCoord.xy + 3.230769 * offset_direction;
return transform_projection * vertex_position;
}
// Fragment Shader:
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif
varying vec2 coordinate2b;
varying vec2 coordinate1b;
varying vec2 coordinate0f;
varying vec2 coordinate1f;
varying vec2 coordinate2f;
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords)
{
vec4 c = vec4(0.0);
c += vec4(0.070270) * Texel(texture, coordinate2b);
c += vec4(0.316216) * Texel(texture, coordinate1b);
c += vec4(0.227027) * Texel(texture, coordinate0f);
c += vec4(0.316216) * Texel(texture, coordinate1f);
c += vec4(0.070270) * Texel(texture, coordinate2f);
return c * color;
}
I am currently guessing the problem is with float precision, but I am not sure what I would have to do to fix that.
Thanks to Columbo, I found out two different problems when running the game on my android.
First was that texture filtering was not linear, this making the scaling optimization not working and having weird problems with scaling.
Second, love2d for android is not supporting gamma correction, so when running on my phone the game was not rendering color correctly and the blur was not visible due to the dark background.
Always check for gamma correction!
I use Vuforia SDK to render the video stream of my phone's camera on the screen.
So the texture is generated by the Vuforia library, not me.
The shaders used to render this background are:
// Vertex Shader
attribute vec4 a_position;
attribute vec2 a_textureCoords;
varying vec2 v_textureCoords;
uniform mat4 u_projectionMatrix;
void main()
{
gl_Position = u_projectionMatrix * a_position;
v_textureCoords = a_textureCoords;
}
// Fragment Shader
varying highp vec2 v_textureCoords;
uniform sampler2D u_currentTexture;
void main()
{
vec4 currentColor = texture2D(u_currentTexture, v_textureCoords);
gl_FragColor = currentColor;
}
Now, I want an overlay in the upper-left corner of the screen:
I don't want this overlay to display only a pink texture, but rather a multiply blend of the pink texture and the background texture. Note that the textures do not have the same coordinates.
But for now, let's forget about the blending and let's just render the background texture in the shader program of the pink texture. So in the end, yes, one should see no difference between the background-only version and the bacground with overlay version.
As you can see (look at the painting and the top of the chair), there is a small offset...
The shaders used to render the overlay are:
// Vertex Shader
attribute vec4 a_position;
attribute vec2 a_currentTextureCoords;
varying vec2 v_currentTextureCoords;
void main()
{
gl_Position = a_position;
v_currentTextureCoords = a_currentTextureCoords;
}
// Fragment Shader
varying highp vec2 v_currentTextureCoords;
uniform sampler2D u_currentTexture;
uniform sampler2D u_backgroundTexture;
void main()
{
vec2 screenSize = vec2(1080.0, 1920.0);
vec2 cameraResolution = vec2(720.0, 1280.0);
vec2 texelSize = vec2(1.0 / screenSize.x, 1.0 / screenSize.y);
vec2 scaleFactor = vec2(cameraResolution.x / screenSize.x, cameraResolution.y / screenSize.y);
vec2 uv = gl_FragCoord.xy * texelSize * scaleFactor;
uv = vec2(scaleFactor.y - uv.y, scaleFactor.x - uv.x);
vec4 backgroundColor = texture2D(u_backgroundTexture, uv);
gl_FragColor = backgroundColor;
}
Are my calculations wrong?
Why do you need this line?
uv = vec2(scaleFactor.y - uv.y, scaleFactor.x - uv.x);
Not sure what arithmetic relationship the absolute texture coordinates have with the scale factor which needs an addition or a subtraction ...
P.S. it's not related to your question, but your shaders will be shorter and easier to read if you just use the vector operations in the language. For example, replace:
vec2 scaleFactor = vec2(cameraResolution.x / screenSize.x, cameraResolution.y / screenSize.y);
... with ...
vec2 scaleFactor = cameraResolution / screenSize;
As long as the vector types are the same length, it will do exactly what you expect with a lot less typing ...
I needed to create some ripling effect for one sprite in my game, here's the vertexShader:
attribute vec4 a_position; // just taking in necessary attributes
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans; // Combination of view and projection matrix
varying vec2 v_texCoords;
void main() {
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position; //as I said, it is sprite so no need for modelMatrix
}
and here's the fragment:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoords;
uniform sampler2D u_texture; //texture of sprite
uniform float time;
void main()
{
vec2 uv;
if (time > 0.0) { // time is > 0.0 when I want the ripling effect to be applied,
vec2 cPos = -1.0 + 2.0 * v_texCoords.xy; // converting tex.Coords to -1 - 1
float cLength = length(cPos); //taking length of it
uv = v_texCoords.xy +( (cPos/cLength)*cos(cLength*12.0-time*4.0)*0.03 ) // just some calculations for the ripling effect
}
else
uv = v_texCoords.xy; // if I don't want to use the ripling effect, I use normal texCoords
vec4 tex = texture2D(u_texture, uv); //sampling texture
gl_FragColor = tex;
}
It all works fine, the performance's fine on PC, but when running it on android, the performance is a lot worse... As you can see, shader's are trivial but they somehow are expensive.. Anyways, sprite I draw has width about 2000 - 4000 px and height 720. Also, when I replace v_texCoords with different vector(for example vec2(1, 1)) in cPos calc: vec2 cPos = -1.0 + 2.0 * v_texCoords.xy; the performance improves heavily..
I don't really know what's so expensive there. If anyone had some advices, I'd be happy. Thanks in advance