So I have 2 shaders that I want to combine. ie, the output of shader 1 (Edge Detection) should be the input of shader 2 (Blur).
Both shaders work perfectly standalone. However, I do not know how to combine them, because the output of shader 1 is a vec4, and shader 2 needs to use texture coordinate and sampler2D.
Shader 1 (Edge Detection)
precision highp float;
varying vec3 n;
varying vec2 uv;
uniform sampler2D tex;
precision mediump float;
void main() {
/*
* Edge Detection
*/
vec3 irgb = texture2D(tex, uv).rgb;
float ResS = 720.;
float ResT = 720.;
vec2 stp0 = vec2(1./ResS, 0.);
vec2 st0p = vec2(0., 1./ResT);
vec2 stpp = vec2(1./ResS, 1./ResT);
vec2 stpm = vec2(1./ResS, -1./ResT);
const vec3 W = vec3(0.2125, 0.7154, 0.0721);
float i00 = dot(texture2D(tex, uv).rgb, W);
float im1m1 = dot(texture2D(tex, uv-stpp).rgb, W);
float ip1p1 = dot(texture2D(tex, uv+stpp).rgb, W);
float im1p1 = dot(texture2D(tex, uv-stpm).rgb, W);
float ip1m1 = dot(texture2D(tex, uv+stpm).rgb, W);
float im10 = dot(texture2D(tex, uv-stp0).rgb, W);
float ip10 = dot(texture2D(tex, uv+stp0).rgb, W);
float i0m1 = dot(texture2D(tex, uv-st0p).rgb, W);
float i0p1 = dot(texture2D(tex, uv+st0p).rgb, W);
float h = -1.*im1p1 - 2.*i0p1 - 1.*ip1p1 + 1.*im1m1 + 2.*i0m1 + 1.*ip1m1;
float v = -1.*im1m1 - 2.*im10 - 1.*im1p1 + 1.*ip1m1 + 2.*ip10 + 1.*ip1p1;
float mag = length(vec2(h, v));
vec3 target = vec3(mag, mag, mag);
vec4 nonWhiteToBlack = vec4(mix(irgb, target, 1.0),1.);
if (nonWhiteToBlack.r < 0.2 && nonWhiteToBlack.g < 0.2 && nonWhiteToBlack.b < 0.2) {
nonWhiteToBlack = vec4(0,0,0,1);
}
gl_FragColor = vec4(nonWhiteToBlack);
}
Shader 2 (Blur)
/*
* blur
*/
vec3 irgb = texture2D(tex, uv).rgb;
float ResS = 720.;
float ResT = 720.;
vec2 stp0 = vec2(1./ResS, 0.);
vec2 st0p = vec2(0., 1./ResT);
vec2 stpp = vec2(1./ResS, 1./ResT);
vec2 stpm = vec2(1./ResS, -1./ResT);
vec3 i00 = texture2D(tex, uv).rgb;
vec3 im1m1 = texture2D(tex, uv-stpp).rgb;
vec3 ip1p1 = texture2D(tex, uv+stpp).rgb;
vec3 im1p1 = texture2D(tex, uv-stpm).rgb;
vec3 ip1m1 = texture2D(tex, uv+stpm).rgb;
vec3 im10 = texture2D(tex, uv-stp0).rgb;
vec3 ip10 = texture2D(tex, uv+stp0).rgb;
vec3 i0m1 = texture2D(tex, uv-st0p).rgb;
vec3 i0p1 = texture2D(tex, uv+st0p).rgb;
vec3 target = vec3(0., 0., 0.);
target += 1.*(im1m1+ip1m1+ip1p1+im1p1);
target += 2.*(im10+ip10+i0p1);
target += 4.*(i00);
target /= 16.;
gl_FragColor = vec4(target, 1.);
Any Help Would Be Appreciated
Related
I have an OpenGL app with a simple shader that run well on an emulator device in Android Studio with API 30 but on my own hardware device (API 30) it doesn't.
The problem is in the fragment shader. This is the code:
#version 100
precision highp float;
struct DirLight {
int on;
vec3 direction;
vec3 ambientColor;
vec3 diffuseColor;
vec3 specularColor;
float specularExponent;
sampler2D shadowMap;
mat4 shadowVPMatrix;
int shadowEnabled;
};
struct PointLight {
int on;
vec3 position;
float constant;
float linear;
float quadratic;
vec3 ambientColor;
vec3 diffuseColor;
vec3 specularColor;
float specularExponent;
sampler2D shadowMap;
mat4 shadowVPMatrix;
int shadowEnabled;
};
#define MAX_NUM_POINT_LIGHTS 8
uniform DirLight uDirLight;
uniform PointLight uPointLights[MAX_NUM_POINT_LIGHTS];
uniform int uNumPointLights;
uniform vec3 uViewPos;
uniform sampler2D uTexture;
uniform int uIsTextured;
varying vec4 vColor;
varying vec4 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoords;
const vec4 bitShifts = vec4(1.0 / (256.0*256.0*256.0), 1.0 / (256.0*256.0), 1.0 / 256.0, 1.0);
vec4 getColor(){
if (uIsTextured != 0){
return texture2D(uTexture,vTexCoords);
}
return vColor;
}
float unpack(vec4 color){
return dot(color, bitShifts);
}
// return 0.0 if in shadow.
// return 1.0 if not in shadow.
float calcShadow(sampler2D shadowMap, vec4 positionFromLight, int shadowEnabled){
if (shadowEnabled == 0){
return 1.0;
}
vec3 positionFromLight3 = positionFromLight.xyz / positionFromLight.w;
positionFromLight3 = (positionFromLight3 + 1.0) / 2.0;
float closestFragmentZ = unpack(texture2D(shadowMap, positionFromLight3.xy));
float currentFragmentZ = positionFromLight3.z;
return float(closestFragmentZ > currentFragmentZ);
}
float diffuseLighting(vec3 normal, vec3 lightDir){
return max(dot(normal, lightDir), 0.0);
}
float specularLighting(vec3 normal, vec3 lightDir, vec3 viewDir, float specularExponent){
vec3 reflectDir = reflect(-lightDir, normal);
return pow(max(dot(viewDir, reflectDir), 0.0), specularExponent);
}
vec4 calcDirLight(vec3 normal, vec3 viewDir){
vec3 lightDir = normalize(-uDirLight.direction);
float diff = diffuseLighting(normal, lightDir);
float spec = specularLighting(normal, lightDir, viewDir, uDirLight.specularExponent);
vec4 color = getColor();
vec4 ambient = vec4(uDirLight.ambientColor, 1.0) * color;
vec4 diffuse = vec4(uDirLight.diffuseColor * diff, 1.0) * color;
vec4 specular = vec4(uDirLight.specularColor * spec, 1.0) * vec4(0.5,0.5,0.5,1.0);
return ambient + (diffuse + specular) * calcShadow(uDirLight.shadowMap, uDirLight.shadowVPMatrix * vPosition, uDirLight.shadowEnabled);
}
float calcAttenuation(PointLight pointLight, float distance){
return 1.0 / (pointLight.constant + pointLight.linear * distance + pointLight.quadratic * (distance * distance));
}
vec4 calcPointLight(PointLight pointLight, vec3 normal, vec3 viewDir){
vec3 d = pointLight.position - vec3(vPosition);
vec3 lightDir = normalize(d);
float diff = diffuseLighting(normal, lightDir);
float spec = specularLighting(normal, lightDir, viewDir, pointLight.specularExponent);
float distance = length(d);
float attenuation = calcAttenuation(pointLight,distance);
vec4 color = getColor();
vec4 ambient = vec4(pointLight.ambientColor, 1.0) * color;
vec4 diffuse = vec4(pointLight.diffuseColor * diff, 1.0) * color;
vec4 specular = vec4(pointLight.specularColor * spec, 1.0) * vec4(0.5,0.5,0.5,1.0);
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
return ambient + (diffuse + specular) * calcShadow(pointLight.shadowMap, pointLight.shadowVPMatrix * vPosition, pointLight.shadowEnabled);
}
void main() {
vec3 normal = normalize(vNormal);
vec3 viewDir = normalize(uViewPos - vec3(vPosition));
vec4 result = vec4(0.0);
if (uDirLight.on == 1){
result = calcDirLight(normal, viewDir);
}
for (int i = 0; i < uNumPointLights; i++){
if (uPointLights[i].on == 1){
result += calcPointLight(uPointLights[i], normal, viewDir);
}
}
gl_FragColor = result;
}
When I run the app on my device logcat shows the following lines
2021-06-24 17:49:14.032 2061-2096/com.outofbound.rhinoengine I/AdrenoGLES-0: Build Config : S P 10.0.7 AArch64
2021-06-24 17:49:14.032 2061-2096/com.outofbound.rhinoengine I/AdrenoGLES-0: Driver Path : /vendor/lib64/egl/libGLESv2_adreno.so
2021-06-24 17:49:14.036 2061-2096/com.outofbound.rhinoengine I/AdrenoGLES-0: PFP: 0x016ee190, ME: 0x00000000
2021-06-24 17:49:14.040 2061-2061/com.outofbound.rhinoengine D/SurfaceView: UPDATE null, mIsCastMode = false
2021-06-24 17:49:14.074 2061-2102/com.outofbound.rhinoengine I/AdrenoGLES-0: ERROR: 0:101: 'viewDir' : undeclared identifier
ERROR: 0:101: 'specularLighting' : no matching overloaded function found
ERROR: 2 compilation errors. No code generated.
2021-06-24 17:49:14.075 2061-2102/com.outofbound.rhinoengine I/AdrenoGLES-0: ERROR: 0:101: 'viewDir' : undeclared identifier
ERROR: 0:101: 'specularLighting' : no matching overloaded function found
ERROR: 2 compilation errors. No code generated.
2021-06-24 17:49:15.316 2061-2085/com.outofbound.rhinoengine W/System: A resource failed to call close.
BUT if I simply rename viewDir to v in main() function
void main() {
vec3 normal = normalize(vNormal);
vec3 v = normalize(uViewPos - vec3(vPosition));
vec4 result = vec4(0.0);
if (uDirLight.on == 1){
result = calcDirLight(normal, v);
}
for (int i = 0; i < uNumPointLights; i++){
if (uPointLights[i].on == 1){
result += calcPointLight(uPointLights[i], normal, v);
}
}
gl_FragColor = result;
}
the error above disappears but the app still doesn't work showing a black screen.
Any tips?
It looks to me that the viewDir issue is a driver bug where it's messed up trying to inline your code.
However, you should be aware that is not a simple shader by OpenGLES 2 standards. As Dpk implied, you cannot assume high precision is available in OpenGLES2.
Additionally, you cannot assume that there's anywhere near enough uniform space for your shader. Try using glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniforms); to see how many uniforms are supported. Devices are allowed to go as low as 16 vec4s, but your shader uses 100s.
I'd suggest you consider switching to OpenGLES 3 or 3.1 if you don't want to worry about some of the tight limits of GLES2. If you persist with OpenGLES2 then maybe cut the shader right back to literally nothing (just return a colour) and gradually build up the functionality.
Also, make sure you are checking for errors on shader compilation and linking and all OpenGLES calls, it can save a lot of time.
try
//#version 100
//precision highp float;
precision mediump float;
and try this
opengles20 may not support INT in param see doc
float on;
//if (uDirLight.on == 1){
if (uDirLight.on == 1.0){
I think the error is related to the array of uniform uniform PointLight uPointLights[MAX_NUM_POINT_LIGHTS];. So I solved using one point light
uniform PointLight uPointLight;.
Anyway I'll try if defining multiple uniform PointLight uPointLightN; with 0 <= N < MAX_NUM_POINT_LIGHTS it still works.
This is my first question here
The thing is I'm programming an app with OpenGL ES in Android Studio, and I've encountered some problems.
The lights seem to work on the emulator, but when building the APK and installing it on an actual device (with Android 5.0, with opengl es version 3.0 and support for glsl es 3.00), it looks weird:
Emulator, Android Studio
Emulator
Phone:
Phone
It looks like the lighting is not done er fragment on the phone or something, I dont know whats happening.
Here are the shaders:
#version 300 es
uniform mat4 M;
uniform mat4 V;
uniform mat4 P;
uniform int boolGUI;
uniform float opacity;
uniform float blackness;
in vec3 vPosition;
in vec2 vUV;
in vec3 vNormal;
//"in vec3 vColor;" +
//Lights
uniform mat4 lMat;
out mat4 frLMat;
out vec3 frColor;
out vec3 frNormalCam;
//out vec3 frLightCam;
out vec3 frEyeCam;
out vec2 oUV;
out float z;
flat out int frBoolGUI;
flat out float frOpacity;
flat out float frBlackness;
out vec3 frPos;
out mat4 frV;
void main() {
mat4 uMVPMatrix = P * V * M;
vec4 vPos;
vPos.xyz = vPosition;
vPos.w = 1.0;
gl_Position = uMVPMatrix * vPos;
frColor = vec3(1.0,1.0,1.0);
//vec3 light = vec3(0.3,-0.3,-0.4);
vec3 light = normalize(vec3(-0.1,-0.3,0.2))*0.6;
light = normalize(-vPos.xyz+vec3(0.0,0.0,1.0));
//frLightCam = (V * vec4(light,0)).xyz;
//frNormal = vNormal;
frNormalCam = ( V * M * vec4(normalize(vNormal),0)).xyz;
frEyeCam = (V*vec4(0,-3,2.5,0)).xyz;
oUV = vUV.xy;
z = vPos.z;
frBoolGUI = boolGUI;
frOpacity = opacity;
frBlackness = blackness;
frPos = (M*vPos).xyz;
frV = V;
frLMat=lMat;
}
And the fragment shader:
#version 300 es
precision mediump float;
in vec3 frColor;
in vec3 frNormalCam;
//in vec3 frLightCam;
in vec3 frEyeCam;
in vec3 frPos;
in vec2 oUV;
in float z;
flat in int frBoolGUI;
flat in float frOpacity;
flat in float frBlackness;
//vec3 l = vec3(1.0,0.1,-0.2);
out vec4 FragColor;
in mat4 frV;
in mat4 frLMat;
uniform sampler2D myTextureSampler;
void main() {
float att = 0.0;
//for(int i=0;i<4;i++) {
int i=0;
if(frLMat[i][3]>0.0) {
vec3 li=frLMat[i].xyz;
vec3 lightVec = -frPos.xyz+li;//vec3(0.0,-0.5,1.5);
vec3 light = normalize(lightVec)*0.6;
vec3 frLightCam = (frV * vec4(light,0)).xyz;
float lightAtt = 0.6/(length(lightVec))+0.4;
float ambient = 0.40;
vec3 E = normalize(frEyeCam);
vec3 R = reflect(-frLightCam,frNormalCam);
float cosAlpha = clamp( dot( E,R ), 0.0,1.0 );
float spec = pow(cosAlpha,4.0)*5.0;
att += ((spec + clamp( dot(frNormalCam,frLightCam)*(1.0-ambient), 0.0,1.0 ))*lightAtt+ambient)*frLMat[i].w;
}
//}
//if(z>1.7) att = att-(z-1.7)*1.7;
//if(frPos.y>2.0) att = att-abs((frPos.y-2.0)*1.7);
FragColor.xyz = texture( myTextureSampler, oUV ).rgb * att;//*lightAtt;// frColor * att;
FragColor.w=texture(myTextureSampler,oUV).w;
vec3 gamma = vec3(1.0/0.85);
gamma = vec3(1.0/0.8);
FragColor.xyz = pow(FragColor.xyz,gamma)*(frBlackness+vec3(0.05))+vec3(0.06);
FragColor.a = frOpacity;
if (frBoolGUI==1) FragColor = texture(myTextureSampler,oUV);
}
(Please excuse the clumsy code, im only testing right now).
Any help would be appreciated
I am developing OpenGLES app for Android and using Perlin noise from Stefan Gustavson. It's animated 2D noise so I use 3D Perlin with time variable as third dimension. And it was all looking good on my Samsung Galaxy Young (API 10), but when I tested it with ASUS MEMO tablet (API 17) I got this glitchy thing with even elipses and square-like areas:-
What could cause such differences between various devices?
Fragment shader code:
precision mediump float;
varying vec2 screenPosition;
uniform vec4 colorFilter;
uniform float time;
vec4 permute(vec4 x)
{
return mod(((x*34.0)+1.0)*x, 289.0);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
vec3 fade(vec3 t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
float noise(vec3 P)
{
vec3 Pi0 = floor(P); // Integer part for indexing
vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
Pi0 = mod(Pi0, 289.0);
Pi1 = mod(Pi1, 289.0);
vec3 Pf0 = fract(P); // Fractional part for interpolation
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.yy, Pi1.yy);
vec4 iz0 = Pi0.zzzz;
vec4 iz1 = Pi1.zzzz;
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 / 7.0;
vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 / 7.0;
vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
g000 *= norm0.x;
g010 *= norm0.y;
g100 *= norm0.z;
g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
g001 *= norm1.x;
g011 *= norm1.y;
g101 *= norm1.z;
g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
return 2.2 * n_xyz;
}
void main() {
float valuer = (noise(vec3(screenPosition.x*4.0, screenPosition.y*3.0, time)));
float val1 = 0.25*(sign(valuer+0.2)-sign(valuer-0.2));
float val2 = 0.125*(sign(valuer+0.4)-sign(valuer-0.4));
outputColor = colorFilter*(val1+val2+0.25);
}
Most likely the Samsung Galaxy Young is executing the math at highp, and the ASUS MEMO tablet is executing at mediump.
OpenGLES devices are not required to support highp in fragment shaders, but the above code looks very much like it will be required.
Is that even a full fragment shader? I thought GLES fragment shaders required you to specify a default precision or required you to specify precision on a per-variable basis. I see neither, so perhaps there is some information omitted.
I am creating game in Libgdx and I'm using OpenGL water shader.
On desktop everything works fine (60 fps without V-Sync), but on Android I have only 1 FPS (tested on Samsung Galaxy S3 Neo and HTC One).
My fragment shader:
#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform float iGlobalTime; // shader playback time (in seconds)
const int NUM_STEPS = 8;
const float PI = 3.1415;
const float EPSILON = 1e-3;
float EPSILON_NRM = 0.1 / iResolution.x;
// sea
const int ITER_GEOMETRY = 3;
const int ITER_FRAGMENT = 5;
const float SEA_HEIGHT = 0.6;
const float SEA_CHOPPY = 4.0;
const float SEA_SPEED = 0.8;
const float SEA_FREQ = 0.16;
const vec3 SEA_BASE = vec3(0.1,0.19,0.22);
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
float SEA_TIME = iGlobalTime * SEA_SPEED;
mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
// math
mat3 fromEuler(vec3 ang) {
vec2 a1 = vec2(sin(ang.x),cos(ang.x));
vec2 a2 = vec2(sin(ang.y),cos(ang.y));
vec2 a3 = vec2(sin(ang.z),cos(ang.z));
mat3 m;
m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
return m;
}
float hash( vec2 p ) {
float h = dot(p,vec2(127.1,311.7));
return fract(sin(h)*43758.5453123);
}
float noise( in vec2 p ) {
vec2 i = floor( p );
vec2 f = fract( p );
vec2 u = f*f*(3.0-2.0*f);
return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), hash( i + vec2(1.0,0.0) ), u.x), mix( hash( i + vec2(0.0,1.0) ), hash( i + vec2(1.0,1.0) ), u.x), u.y);
}
// lighting
float diffuse(vec3 n,vec3 l,float p) {
return pow(dot(n,l) * 0.4 + 0.6,p);
}
float specular(vec3 n,vec3 l,vec3 e,float s) {
float nrm = (s + 8.0) / (3.1415 * 8.0);
return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
}
// sky
vec3 getSkyColor(vec3 e) {
e.y = max(e.y,0.0);
vec3 ret;
ret.x = pow(1.0-e.y,2.0);
ret.y = 1.0-e.y;
ret.z = 0.6+(1.0-e.y)*0.4;
return ret;
}
// sea
float sea_octave(vec2 uv, float choppy) {
uv += noise(uv);
vec2 wv = 1.0-abs(sin(uv));
vec2 swv = abs(cos(uv));
wv = mix(wv,swv,wv);
return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
}
float map(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz; uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_GEOMETRY; i++) {
d = sea_octave((uv+SEA_TIME)*freq,choppy);
d += sea_octave((uv-SEA_TIME)*freq,choppy);
h += d * amp;
uv *= octave_m; freq *= 1.9; amp *= 0.22;
choppy = mix(choppy,1.0,0.2);
}
return p.y - h;
}
float map_detailed(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz; uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_FRAGMENT; i++) {
d = sea_octave((uv+SEA_TIME)*freq,choppy);
d += sea_octave((uv-SEA_TIME)*freq,choppy);
h += d * amp;
uv *= octave_m; freq *= 1.9; amp *= 0.22;
choppy = mix(choppy,1.0,0.2);
}
return p.y - h;
}
vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {
float fresnel = 1.0 - max(dot(n,-eye),0.0);
fresnel = pow(fresnel,3.0) * 0.65;
vec3 reflected = getSkyColor(reflect(eye,n));
vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;
vec3 color = mix(refracted,reflected,fresnel);
float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
color += vec3(specular(n,l,eye,60.0));
return color;
}
// tracing
vec3 getNormal(vec3 p, float eps) {
vec3 n;
n.y = map_detailed(p);
n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
n.y = eps;
return normalize(n);
}
float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {
float tm = 0.0;
float tx = 10000.0;
float hx = map(ori + dir * tx);
if(hx > 0.0) return tx;
float hm = map(ori + dir * tm);
float tmid = 0.0;
for(int i = 0; i < NUM_STEPS; i++) {
tmid = mix(tm,tx, hm/(hm-hx));
p = ori + dir * tmid;
float hmid = map(p);
if(hmid < 0.0) {
tx = tmid;
hx = hmid;
} else {
tm = tmid;
hm = hmid;
}
}
return tmid;
}
// main
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 uv = fragCoord.xy / iResolution.xy;
uv = uv * 2.0 - 1.65;
uv.x *= iResolution.x / iResolution.y;
float time = iGlobalTime * 0.3;
// ray
vec3 ang = vec3(0,0,0);
vec3 ori = vec3(0.0,20,0);
vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.15;
dir = normalize(dir) * fromEuler(ang);
// tracing
vec3 p;
heightMapTracing(ori,dir,p);
vec3 dist = p - ori;
vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
vec3 light = normalize(vec3(0.0,1.0,0.8));
// color
vec3 color = mix(
getSkyColor(dir),
getSeaColor(p,n,light,dir,dist),
pow(smoothstep(0.0,-0.05,dir.y),.3));
// post
fragColor = vec4(pow(color,vec3(0.75)), 1.0);
}
void main() {
vec4 color;
mainImage(color, gl_FragCoord.xy);
color.w = 1.0;
gl_FragColor = color;
}
My vertex shader:
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
void main() {
v_color = a_color;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
My Libgdx code:
private Mesh mesh;
private ShaderProgram shader;
private float time = 0;
private OrthographicCamera camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
#Override
public void show() {
shader = new ShaderProgram(Gdx.files.internal("seaVertex.txt"), Gdx.files.internal("seaFragment.txt"));
shader.pedantic = false;
mesh = new Mesh(true, 4, 6, new VertexAttribute(Usage.Position, 2, "a_position"));
mesh.setVertices(new float[]{-Gdx.graphics.getWidth() / 2, -Gdx.graphics.getHeight() / 2,
Gdx.graphics.getWidth() / 2, -Gdx.graphics.getHeight() / 2,
-Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2,
Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2});
mesh.setIndices(new short[]{0, 2, 3, 0, 3, 1});
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
time += delta;
if (shader.isCompiled()){
shader.begin();
shader.setUniformMatrix("u_projTrans", camera.combined);
shader.setUniformf("iGlobalTime", time);
shader.setUniformf("iResolution", new Vector3(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), 0));
mesh.render(shader, GL20.GL_TRIANGLES);
shader.end();
}
}
Any ideas?
I am not familiar with Libgdx but I have a basic knowledge of OpenGL and graphics rendering in general(and I worked with this shader once) so here are my thoughts (I'm guessing you don't have a deep understand of the method this shader uses to generate water):
Your current "water shader" uses Raymarch wich is a very expensive way to make graphics and is usually used for demo scenes and some basic elements in most graphics scenes. Noise functions are very expensive and you are currently calling it a bunch of times for every ray, wich is also expensive because the raymarch procedure needs to call it a bunch of times to detect intersection with the water.
You are making very hard to create a game using this approach since combination of raymarched elements with tradicional OpenGL rendering system is not trivial, unless you are willing to work the hole game in procedural form, wich is also difficult.
I suggest you try other ways to render water that are not procedural, take a look at "thinMatrix" series on generating water in OpenGL, he uses Java but it should not be difficult to port it : https://www.youtube.com/watch?v=HusvGeEDU_U&list=PLRIWtICgwaX23jiqVByUs0bqhnalNTNZh
Good Luck!
I'm developing a game which uses glsl shader to produce terrain based on the vertex "height". This works very very fast on desktop but quite slow on android tablet. This is of course natural. I'm still a noob with GLSL and OpenGL. This is the result from the shader:
lowp setting doesn't affect that much for performance. I have now tuned this and frame rate is ~20. Can someone give ideas how to improve this shader code:
Fragment shader:
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying float EyeVertexZ;
//varying LOWP float Explored;
varying LOWP vec2 v_texCoords;
uniform LOWP float u_offset;
uniform sampler2D u_texture;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
uniform vec2 resolution;
void main() {
LOWP vec4 color;
vec4 colorGrass = vec4(0.631, 0.69, 0.49 ,1.0);
float a;
float height = EyeVertexZ;
color = colorGrass;
//if height under 0.67 --> water
if(height < 0.67)
{
vec2 coords = v_texCoords.st;
coords.x -= u_offset;
LOWP vec3 nColor;
float bumpAmount = 0.2;
//bumpmapping texture
nColor = texture2D(u_texture, coords).rgb;
//water color, deep water darker
color = vec4(0.3, 0.4 + EyeVertexZ*0.33,0.6 + EyeVertexZ*0.33 ,1.0);
bool useNormals = true;
bool useShadow = true;
bool yInvert = false;
vec3 lightColor = vec3(1.0, 1.0, 1.0);
vec3 attenuation = vec3(0.5, 0.5,0.5);
vec3 ambientColor = vec3(1.0, 1.0, 1.0);
vec4 v_color = vec4(1.0, 1.0, 1.0, 1.0);
float ambientIntensity = 0.3;
vec3 light = vec3(resolution.x, 0.0, 0.5);
//some bump map programs will need the Y value flipped..
nColor.g = yInvert ? 1.0 - nColor.g : nColor.g;
//this is for debugging purposes, allowing us to lower the intensity of our bump map
LOWP vec3 nBase = vec3(0.5, 0.5, 1.0);
nColor = mix(nBase, nColor, bumpAmount);
//normals need to be converted to [-1.0, 1.0] range and normalized
LOWP vec3 normal = normalize(nColor * 2.0 - 1.0);
//here we do a simple distance calculation
LOWP vec3 deltaPos = vec3( (light.xy - gl_FragCoord.xy) / resolution.xy, light.z);
LOWP vec3 lightDir = normalize(deltaPos);
LOWP float lambert = useNormals ? clamp(dot(normal, lightDir), 0.0, 1.0) : 1.0;
//now let's get a nice little falloff
LOWP float d = sqrt(dot(deltaPos, deltaPos));
LOWP float att = useShadow ? 1.0 / ( attenuation.x + (attenuation.y*d) + (attenuation.z*d*d) ) : 1.0;
LOWP vec3 result = (ambientColor * ambientIntensity) + (lightColor.rgb * lambert) * att;
result *= color.rgb;
color = v_color * vec4(result, color.a);
}
a = height - 0.65;
a = max(a,0.0);
color = mix(color, texture2D(u_texture2, v_texCoords),a < 0.02 ? a*50.0 : 0.0);
if(height > 0.67 && height <= 0.70)
{
a = height-0.67;
color = mix( texture2D(u_texture2, v_texCoords),color, a*33.0);
}
gl_FragColor = color; //mix(color, hex, hex.a);
}
Vertex shader
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
attribute vec4 a_position;
attribute vec2 a_texCoords;
uniform mat4 u_worldView;
varying LOWP float noise;
varying float EyeVertexZ;
varying LOWP float Explored;
varying vec2 v_texCoords;
void main() {
gl_Position = u_worldView*a_position;
EyeVertexZ = a_position.z;
v_texCoords = a_texCoords;
}