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!
Related
There are dozens of image filters written for the Android version of our app in GLSL (ES). As of iOS 12, OpenGL is deprecated, and CIFilter kernels have to be written in Metal.
I had some previous background in OpenGL, however writing CIFilter kernels in Metal is new to me.
Here is one of the filters. Could you help me in translating it to Metal as a CIFilter kernel? That would provide a good example for me so I could translate others.
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform samplerExternalOES sTexture;
uniform float texelWidth;
uniform float texelHeight;
uniform float intensivity;
void main() {
float SIZE = 1.25 + (intensivity / 100.0)*2.0;
vec4 color;
float min = 1.0;
float max = 0.0;
float val = 0.0;
for (float x = -SIZE; x < SIZE; x++) {
for (float y = -SIZE; y < SIZE; y++) {
color = texture2D(sTexture, vTextureCoord + vec2(x * texelWidth, y * texelHeight));
val = (color.r + color.g + color.b) / 3.;
if (val > max) { max = val; } else if (val < min) { min = val; }
}
}
float range = 5. * (max - min);
gl_FragColor = vec4(pow(1. - range, SIZE * 1.5));
gl_FragColor = vec4((gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3. > 0.75 ? vec3(1.) : gl_FragColor.rgb, 1.);
}
Here's the Metal source for a kernel that attempts to replicate your described filter:
#include <metal_stdlib>
#include <CoreImage/CoreImage.h>
using namespace metal;
extern "C" {
namespace coreimage {
float4 sketch(sampler src, float texelWidth, float texelHeight, float intensity40) {
float size = 1.25f + (intensity40 / 100.0f) * 2.0f;
float minVal = 1.0f;
float maxVal = 0.0f;
for (float x = -size; x < size; ++x) {
for (float y = -size; y < size; ++y) {
float4 color = src.sample(src.coord() + float2(x * texelWidth, y * texelHeight));
float val = (color.r + color.g + color.b) / 3.0f;
if (val > maxVal) {
maxVal = val;
} else if (val < minVal) {
minVal = val;
}
}
}
float range = 5.0f * (maxVal - minVal);
float4 outColor(pow(1.0f - range, size * 1.5f));
outColor = float4((outColor.r + outColor.g + outColor.b) / 3.0f > 0.75f ? float3(1.0f) : outColor.rgb, 1.0f);
return outColor;
}
}
}
I assume you're already familiar with the basics of how to correctly build Metal shaders into a library that can be loaded by Core Image.
You can instantiate your kernel at runtime by loading the default Metal library and requesting the "sketch" function (the name is arbitrary, so long as it matches the kernel source):
NSURL *libraryURL = [NSBundle.mainBundle URLForResource:#"default" withExtension:#"metallib"];
NSData *libraryData = [NSData dataWithContentsOfURL:libraryURL];
NSError *error;
CIKernel *kernel = [CIKernel kernelWithFunctionName:#"sketch" fromMetalLibraryData:libraryData error:&error];
You can then apply this kernel to an image by wrapping it in your own CIFilter subclass, or just invoke it directly:
CIImage *outputImage = [kernel applyWithExtent:CGRectMake(0, 0, width, height)
roiCallback:^CGRect(int index, CGRect destRect)
{ return destRect; }
arguments:#[inputImage, #(1.0f/width), #(1.0f/height), #(60.0f)]];
I've tried to select sensible defaults for each of the arguments (the first of which should be an instance of CIImage), but of course these can be adjusted to taste.
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
I have a shader of atmospheric scattering that I found through the Internet. In fact, this is the WebGL(three.js) shader. I try to run it on mobile devices. I found out that it works great on Qualcomm and KIRIN chipsets when I use hihgp precision and doesn't work on Mali chipsets at all (just black screen). Athouth, it compiles without any errors. I assume that this happens because Mali GPUs (or similar ones) have not enough highp precision for this shader. If I use mediump precision it doesn't work on all devices (just black screen). Is it possible to make this(any) shader works using mediump precision? Or how to make this shader works on all popular chipsets?
Vertex shader:
attribute vec3 a_position;
attribute vec2 a_texCoord0;\n"+
uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
varying vec3 vWorldPosition;
void main() {
vec4 worldPosition = u_worldTrans * vec4( a_position, 1.0 );
vWorldPosition = worldPosition.xyz;
gl_Position = u_projTrans * worldPosition;
}
Fragment shader:
#ifdef GL_ES
precision highp float;
#endif
uniform vec3 sunPosition;
varying vec3 vWorldPosition;
const float turbidity = 3.2;
const float reileigh = 2.7;
const float luminance = 1.0;
const float mieCoefficient = 0.007;
const float mieDirectionalG = .990;
#define e 2.718281828459
#define pi 3.141592653589
#define n 1.0003
#define pn 0.035
const vec3 lambda = vec3(0.00000068, 0.00000055, 0.00000045);
const vec3 K = vec3(0.686, 0.678, 0.666);
const vec3 up = vec3(0.0, 1.0, 0.0);
#define v 4.0
#define rayleighZenithLength 8400.0
#define mieZenithLength 1250.0
#define EE 1000.0
#define sunAngularDiameterCos 0.999956676946
#define cutoffAngle pi/1.95
#define steepness 1.5
vec3 simplifiedRayleigh()
{
return 0.0005 / vec3(94., 40., 18.);
}
float rayleighPhase(float cosTheta)
{
return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));
}
vec3 totalMie(vec3 lambda, vec3 K, float T)
{
float val = 10E-18;
float c = (0.2 * T ) * val;
return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;
}
float hgPhase(float cosTheta, float g)
{
return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(abs(1.0 - 2.0*g*cosTheta + pow(g, 2.0)), 1.5));
}
float sunIntensity(float zenithAngleCos)
{
return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));
}
#define A 0.15
#define B 0.50
#define C 0.10
#define D 0.20
#define E 0.02
#define F 0.30
#define W 1000.0
vec3 Uncharted2Tonemap(vec3 x)
{
return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
}
void main()
{
vec3 cameraPos = vec3(0., 0., -500.);
float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/4000.0)),0.0,1.0);
float reileighCoefficient = reileigh - (1.0* (1.0-sunfade));
vec3 sunDirection = normalize(sunPosition);
float sunE = sunIntensity(dot(sunDirection, up));
vec3 betaR = simplifiedRayleigh() * reileighCoefficient;
vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;
float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos))));
float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(abs(93.885 - ((zenithAngle * 180.0) / pi)), -1.253));
float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(abs(93.885 - ((zenithAngle * 180.0) / pi)), -1.253));
vec3 Fex = exp(-(betaR * sR + betaM * sM));
float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection);
float rPhase = rayleighPhase(cosTheta*0.5+0.5);
vec3 betaRTheta = betaR * rPhase;
float mPhase = hgPhase(cosTheta, mieDirectionalG);
vec3 betaMTheta = betaM * mPhase;
vec3 Lin = pow(abs(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex)),vec3(1.5));
Lin *= mix(vec3(1.0),pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up,sunDirection),5.0),0.0,1.0));
vec3 L0 = vec3(0.1) * Fex;
float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta);
L0 += (sunE * 19000.0 * Fex)*sundisk;
vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));
vec3 texColor = (Lin+L0);
texColor *= 0.04 ;
texColor += vec3(0.0,0.001,0.0025)*0.3;
vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);
vec3 color = curr*whiteScale;
vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade))));
gl_FragColor.rgb = retColor;
gl_FragColor.a = 1.0;
}
I'm using LibGDX, so my shader class is bellow:
public class SkyShader implements Shader{
ShaderProgram program;
Camera specCamera;
int WIDTH, HEIGHT;
int u_projTrans;
int u_worldTrans;
int u_sunPosition;
int u_res;
int u_luminance;
int u_turbidity;
int u_reileigh;
int u_mieCoefficient;
int u_mieDirectionalG;
public Vector3 sunDirection = new Vector3();
Matrix4 viewInvTraMatrix = new Matrix4();
#Override
public void init() {
program = new ShaderProgram(vert, frag);
if (!program.isCompiled())throw new RuntimeException(program.getLog());
else Gdx.app.log("sky program", "shader compiled successfully!");
u_projTrans = program.getUniformLocation("u_projTrans");
u_worldTrans = program.getUniformLocation("u_worldTrans");
u_sunPosition = program.getUniformLocation("sunPosition");
u_res = program.getUniformLocation("u_res");
}
#Override
public void dispose() {
if(program!=null){
program.dispose();
program = null;
}
}
static final float delta = 0.0028526667f;
Matrix4 projectionMatrix = new Matrix4();
#Override
public void begin(Camera camera, RenderContext context) {
program.begin();
projectionMatrix.set(camera.projection);
if(Settings.landscape)
projectionMatrix.rotate(0, 1, 0, (Settings.EarthRotation-100) * .4f);
else projectionMatrix.rotate(0, 1, 0, (Settings.EarthRotation-100) * .7f);
program.setUniformMatrix(u_projTrans, projectionMatrix);
if(Settings.EnableSlightShadowGradient)
program.setUniformf(u_res, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
float rotation = Settings.EarthRotation * delta -.0375f;//- .00674f;
float azimuth = rotation; //_azimuth;
float inclination = .3f; //skyParams[i][4];
float theta = (float) (Math.PI * ( inclination - 0.5f ));
float phi = (float) (2f * Math.PI * ( azimuth - 0.5f ));
float distance = 4000;//400000;
sunDirection.x = (float) (distance * Math.cos( phi ));
sunDirection.y = (float) (distance * Math.sin( phi ) * Math.sin( theta ));
sunDirection.z = (float) (distance * Math.sin( phi ) * Math.cos( theta ));
context.setDepthTest(GL20.GL_LEQUAL);
context.setCullFace(GL20.GL_FRONT);
}
#Override
public boolean canRender(Renderable arg0) {
return true;
}
#Override
public int compareTo(Shader arg0) {
return 0;
}
#Override
public void end() {
program.end();
}
#Override
public void render(Renderable renderable) {
program.setUniformMatrix(u_worldTrans, renderable.worldTransform);
program.setUniformf(u_sunPosition, sunDirection);
renderable.mesh.render(program, renderable.primitiveType,
renderable.meshPartOffset, renderable.meshPartSize);
}
}
And finally, I render it using the following code:
modelBatch.begin(cam);
modelBatch.render(sphere, skyShader);
modelBatch.end();
Where sphere is:
public ModelInstance createSphere() {
ModelBuilder modelBuilder = new ModelBuilder();
float radius = 4500;//450000;
Model tempSphere;
tempSphere = modelBuilder.createSphere(radius, radius, radius, 32, 8,
new Material(),
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.TextureCoordinates
);
return new ModelInstance(tempSphere,0,0,0);
}
Original shader is here
EDIT
If I run original shader(link is above) on problem devices in browser it works perfect!
Thanks in advance.
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 trying to make "burning star" impression on android game I have been developing with little help of noise function (simplex noise in this case). Unfortunately I cant use 3d textures as they are in gles extension and android packages doesn't have them included.
Only option left for me is therefore calculating noise function in fragment shader. Code provided below runs smoothly or acceptably (20-60fps) on HTC Desire Z andLG optimus one. With same program on Motorola XOOM (which have tegra2 chipset) however I get fraction(1-3) of fps even when displaying only small part of object.
Thing we tried so far:
meddling with precision(lowp-higp), both in first line directive and specifying for each occurrence of float/vec separately
commenting parts of noise function - it seem that there isn't any particular bottleneck, its combination of all things together
googling problems related to tegra, floating point in shaders etc
This is stripped down part of code needed for reproduction of this behavior. Note that on XOOM there are some artifacts which we believe is caused by 16bit floating operations in tegra.
precision mediump float;
#define pi 3.141592653589793238462643383279
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
// noise functions.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : ijm
// Lastmod : 20110822 (ijm)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v)
{
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
uniform vec3 color1;
uniform vec3 color2;
uniform float t;
varying vec3 vTextureCoord;
void main()
{
float t = 0.5; //mod(t, 3.0);
float x = (vTextureCoord.x)*2.0;
float y = -(vTextureCoord.y)*2.0;
float r = sqrt(x * x + y * y);
gl_FragColor = vec4(0.0,0.0,0.0,0.0);
if(r<= 1.0){
float n = snoise( vec3(vec2(x,y), Mr_T*3.3 ) );
gl_FragColor = vec4( mix(color1,color2, abs(n) ) ,1.0);
}
}
I was facing the same problem for tegra2 a while ago. Read 1.3 : http://www.opengl.org/wiki/GLSL_:_common_mistakes. and feel the pain. In my case fps went up twice but it still sucked.