Opengl double overflow - opengl

I have double 'radius' = 2.0E-45, when i set it to ~2.0E-46 calculation collapse resulting in white screen. So seems like issue is overflow. I wrote the same algorithm but using nubma cuda and f64 (double precision) 'radius'. And everything works fine. I am using f32 texture buffer for 'depth_array' (there is no float64 dtype for this), but numba implementation works fine with f32, and opengl implementation also works fine until 'radius' bigger than ~2.0E-46. Why numba implementation works, while opengl not? I want to stick with opengl. Is there any possibility to fix it?
I only put in parts that use the 'radius'. All other variables are double type. (code is messy and just a scratch)
#version 150
#extension GL_ARB_gpu_shader_fp64 : enable
double radius = 2.0E-45;
...
dvec2 pixel = dvec2(gl_FragCoord.xy) + dvec2(-0.5+(double(x)+0.5)/double(AA),-0.5+(double(y)+0.5)/double(AA));
dvec2 c = pixel/dvec2(width, height) * dvec2(radius, radius) + dvec2(-radius/2, -radius/2);
color.rgb += sample(c);
...
vec3 sample(dvec2 dn)
{
vec3 color = vec3(0.0,0.0,0.0);
dvec2 d0 = dn;
double zn_size = 0.0;
int i = 0;
while (i < depth)
{
int x = i % depth;
dvec2 value = dvec2(texelFetch(depth_array, x).rg);
dn = complex_mul(dn, value + dn);
dn = dn + d0;
i++;
x = i % depth;
value = dvec2(texelFetch(depth_array, x).rg);
dvec2 zn = value * 0.5 + dn;
zn_size = dot(zn, zn);
if (zn_size > r)
{
double fraciter = (zn_size-r)/(r2-r);
double iter = double(i) - fraciter;
double m = sqrt(iter)*mul*2.0;
color = sin(vec3(.1, .15, .2)*float(m)*0.5)*.5+0.5;
break;
}
}
return color;
}

In GLSL, the literal value 2.0E-45 has the type float. That means the value will be squashed into the valid range of a float before it gets assigned to a value.
If you want a literal to be a double, then it needs to use the proper suffix: 2.0E-45lf.

Related

How to do dynamic loop in WebGL GLSL [duplicate]

I have the a webgl blur shader:
precision mediump float;
precision mediump int;
uniform sampler2D u_image;
uniform float blur;
uniform int u_horizontalpass; // 0 or 1 to indicate vertical or horizontal pass
uniform float sigma; // The sigma value for the gaussian function: higher value means more blur
// A good value for 9x9 is around 3 to 5
// A good value for 7x7 is around 2.5 to 4
// A good value for 5x5 is around 2 to 3.5
// ... play around with this based on what you need :)
varying vec4 v_texCoord;
const vec2 texOffset = vec2(1.0, 1.0);
// uniform vec2 texOffset;
const float PI = 3.14159265;
void main() {
vec2 p = v_texCoord.st;
float numBlurPixelsPerSide = blur / 2.0;
// Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
vec3 incrementalGaussian;
incrementalGaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma);
incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
float coefficientSum = 0.0;
// Take the central sample first...
avgValue += texture2D(u_image, p) * incrementalGaussian.x;
coefficientSum += incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
// Go through the remaining 8 vertical samples (4 on each side of the center)
for (float i = 1.0; i <= numBlurPixelsPerSide; i += 1.0) {
avgValue += texture2D(u_image, p - i * texOffset) * incrementalGaussian.x;
avgValue += texture2D(u_image, p + i * texOffset) * incrementalGaussian.x;
coefficientSum += 2.0 * incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
}
gl_FragColor = avgValue / coefficientSum;
}
When I build, I get the following error message:
webgl-renderer.js?2eb3:137 Uncaught could not compile shader:ERROR:
0:38: 'i' : Loop index cannot be compared with non-constant expression
I have also tried to use just the uniform float blur to compare i to. Is there any way to fix this?
The problem is further detailed here: https://www.khronos.org/webgl/public-mailing-list/archives/1012/msg00063.php
The solution that I've found looking around is to only use a constant expression when comparing a loop var. This doesn't fit with what I need to do which is vary how many times I'm looping based on the blur radius.
Any thoughts on this?
This happens because on some hardware, GLSL loops are un-rolled into native GPU instructions. This means there needs to be a hard upper limit to the number of passes through the for loop, that governs how many copies of the loop's inner code will be generated. If you replace numBlurPixelsPerSide with a const float or even a #define directive, and the shader compiler can then determine the number of passes at compile time, and generate the code accordingly. But with a uniform there, the upper limit is not known at compile time.
There's an interesting wrinkle in this rule: You're allowed to break or call an early return out of a for loop, even though the max iterations must be discernible at compile time. For example, consider this tiny Mandelbrot shader. This is hardly the prettiest fractal on GLSL Sandbox, but I chose it for its small size:
precision mediump float;
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
varying vec2 surfacePosition;
const float max_its = 100.;
float mandelbrot(vec2 z){
vec2 c = z;
for(float i=0.;i<max_its;i++){ // for loop is here.
if(dot(z,z)>4.) return i; // conditional early return here.
z = vec2(z.x*z.x-z.y*z.y,2.*z.x*z.y)+c;
}
return max_its;
}
void main( void ) {
vec2 p = surfacePosition;
gl_FragColor = vec4(mandelbrot(p)/max_its);
}
In this example, max_its is a const so the compiler knows the upper limit and can un-roll this loop if it needs to. Inside the loop, a return statement offers a way to leave the loop early for pixels that are outside of the Mandelbrot set.
You still don't want to set the max iterations too high, as this can produce a lot of GPU instructions and possibly hurt performance.
Try something like this:
const float MAX_ITERATIONS = 100.0;
// Go through the remaining 8 vertical samples (4 on each side of the center)
for (float i = 1.0; i <= MAX_ITERATIONS; i += 1.0) {
if (i >= numBlurPixelsPerSide){break;}
avgValue += texture2D(u_image, p - i * texOffset) * incrementalGaussian.x;
avgValue += texture2D(u_image, p + i * texOffset) * incrementalGaussian.x;
coefficientSum += 2.0 * incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
}
Sometimes you can use my very simple solving of issue.
My fragment of the shader source code:
const int cloudPointsWidth = %s;
for ( int i = 0; i < cloudPointsWidth; i++ ) {
//TO DO something
}
You can see '%' : syntax error above. But I am replace %s to a number in my javascript code before use my shader. For example:
vertexCode = vertexCode.replace( '%s', 10 );
vertexCode is my shader source code.
Everytime if I want to change cloudPointsWidth, I am destroying my old shader and creating new shader with new cloudPointsWidth .
Hope sometimes my solving can to help you.
You can just do a for loop with large constant number and use a break.
for(int i = 0; i < 1000000; ++i)
{
// your code here
if(i >= n){
break;
}
}
I've had similar problem with image downsampling shader. The code is basically the same:
for (int dx = -2 * SCALE_FACTOR; dx < 2 * SCALE_FACTOR; dx += 2) {
for (int dy = -2 * SCALE_FACTOR; dy < 2 * SCALE_FACTOR; dy += 2) {
/* accumulate fragment's color */
}
}
What I've ended up doing is using preprocessor and creating separate shader programs for every SCALE_FACTOR used (luckily, only 4 was needed). To achieve that, a small helper function was implemented to add #define ... statements to shader code:
function insertDefines (shaderCode, defines) {
var defineString = '';
for (var define in defines) {
if (defines.hasOwnProperty(define)) {
defineString +=
'#define ' + define + ' ' + defines[define] + '\n';
}
}
var versionIdx = shaderCode.indexOf('#version');
if (versionIdx == -1) {
return defineString + shaderCode;
}
var nextLineIdx = shaderCode.indexOf('\n', versionIdx) + 1;
return shaderCode.slice(0, nextLineIdx) +
defineString +
shaderCode.slice(nextLineIdx);
}
The implementation is a bit tricky because if the code already has #version preprocessor statement in it, all other statements have to follow it.
Then I've added a check for SCALE_FACROR being defined:
#ifndef SCALE_FACTOR
# error SCALE_FACTOR is undefined
#endif
And in my javascript code I've done something like this:
var SCALE_FACTORS = [4, 8, 16, 32],
shaderCode, // the code of my shader
shaderPrograms = SCALE_FACTORS.map(function (factor) {
var codeWithDefines = insertDefines(shaderCode, { SCALE_FACTOR: factor });
/* compile shaders, link program, return */
});
I use opengl es3 on android and solve this problem by using extension above the beginning of program like this:
#extension GL_EXT_gpu_shader5 : require
I don't know whether it work on webGL, but you can try it.
Hope it can help.
You can also use template litterals to set the length of the loop
onBeforeCompile(shader) {
const array = [1,2,3,4,5];
shader.uniforms.myArray = { value: array };
let token = "#include <begin_vertex>";
const insert = `
uniform float myArray[${array.length}];
for ( int i = 0; i < ${array.length}; i++ ) {
float test = myArray[ i ];
}
`;
shader.vertexShader = shader.vertexShader.replace(token, token + insert);
}

WebGL: Loop index cannot be compared with non-constant expression

I have the a webgl blur shader:
precision mediump float;
precision mediump int;
uniform sampler2D u_image;
uniform float blur;
uniform int u_horizontalpass; // 0 or 1 to indicate vertical or horizontal pass
uniform float sigma; // The sigma value for the gaussian function: higher value means more blur
// A good value for 9x9 is around 3 to 5
// A good value for 7x7 is around 2.5 to 4
// A good value for 5x5 is around 2 to 3.5
// ... play around with this based on what you need :)
varying vec4 v_texCoord;
const vec2 texOffset = vec2(1.0, 1.0);
// uniform vec2 texOffset;
const float PI = 3.14159265;
void main() {
vec2 p = v_texCoord.st;
float numBlurPixelsPerSide = blur / 2.0;
// Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
vec3 incrementalGaussian;
incrementalGaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma);
incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
float coefficientSum = 0.0;
// Take the central sample first...
avgValue += texture2D(u_image, p) * incrementalGaussian.x;
coefficientSum += incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
// Go through the remaining 8 vertical samples (4 on each side of the center)
for (float i = 1.0; i <= numBlurPixelsPerSide; i += 1.0) {
avgValue += texture2D(u_image, p - i * texOffset) * incrementalGaussian.x;
avgValue += texture2D(u_image, p + i * texOffset) * incrementalGaussian.x;
coefficientSum += 2.0 * incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
}
gl_FragColor = avgValue / coefficientSum;
}
When I build, I get the following error message:
webgl-renderer.js?2eb3:137 Uncaught could not compile shader:ERROR:
0:38: 'i' : Loop index cannot be compared with non-constant expression
I have also tried to use just the uniform float blur to compare i to. Is there any way to fix this?
The problem is further detailed here: https://www.khronos.org/webgl/public-mailing-list/archives/1012/msg00063.php
The solution that I've found looking around is to only use a constant expression when comparing a loop var. This doesn't fit with what I need to do which is vary how many times I'm looping based on the blur radius.
Any thoughts on this?
This happens because on some hardware, GLSL loops are un-rolled into native GPU instructions. This means there needs to be a hard upper limit to the number of passes through the for loop, that governs how many copies of the loop's inner code will be generated. If you replace numBlurPixelsPerSide with a const float or even a #define directive, and the shader compiler can then determine the number of passes at compile time, and generate the code accordingly. But with a uniform there, the upper limit is not known at compile time.
There's an interesting wrinkle in this rule: You're allowed to break or call an early return out of a for loop, even though the max iterations must be discernible at compile time. For example, consider this tiny Mandelbrot shader. This is hardly the prettiest fractal on GLSL Sandbox, but I chose it for its small size:
precision mediump float;
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
varying vec2 surfacePosition;
const float max_its = 100.;
float mandelbrot(vec2 z){
vec2 c = z;
for(float i=0.;i<max_its;i++){ // for loop is here.
if(dot(z,z)>4.) return i; // conditional early return here.
z = vec2(z.x*z.x-z.y*z.y,2.*z.x*z.y)+c;
}
return max_its;
}
void main( void ) {
vec2 p = surfacePosition;
gl_FragColor = vec4(mandelbrot(p)/max_its);
}
In this example, max_its is a const so the compiler knows the upper limit and can un-roll this loop if it needs to. Inside the loop, a return statement offers a way to leave the loop early for pixels that are outside of the Mandelbrot set.
You still don't want to set the max iterations too high, as this can produce a lot of GPU instructions and possibly hurt performance.
Try something like this:
const float MAX_ITERATIONS = 100.0;
// Go through the remaining 8 vertical samples (4 on each side of the center)
for (float i = 1.0; i <= MAX_ITERATIONS; i += 1.0) {
if (i >= numBlurPixelsPerSide){break;}
avgValue += texture2D(u_image, p - i * texOffset) * incrementalGaussian.x;
avgValue += texture2D(u_image, p + i * texOffset) * incrementalGaussian.x;
coefficientSum += 2.0 * incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
}
Sometimes you can use my very simple solving of issue.
My fragment of the shader source code:
const int cloudPointsWidth = %s;
for ( int i = 0; i < cloudPointsWidth; i++ ) {
//TO DO something
}
You can see '%' : syntax error above. But I am replace %s to a number in my javascript code before use my shader. For example:
vertexCode = vertexCode.replace( '%s', 10 );
vertexCode is my shader source code.
Everytime if I want to change cloudPointsWidth, I am destroying my old shader and creating new shader with new cloudPointsWidth .
Hope sometimes my solving can to help you.
You can just do a for loop with large constant number and use a break.
for(int i = 0; i < 1000000; ++i)
{
// your code here
if(i >= n){
break;
}
}
I've had similar problem with image downsampling shader. The code is basically the same:
for (int dx = -2 * SCALE_FACTOR; dx < 2 * SCALE_FACTOR; dx += 2) {
for (int dy = -2 * SCALE_FACTOR; dy < 2 * SCALE_FACTOR; dy += 2) {
/* accumulate fragment's color */
}
}
What I've ended up doing is using preprocessor and creating separate shader programs for every SCALE_FACTOR used (luckily, only 4 was needed). To achieve that, a small helper function was implemented to add #define ... statements to shader code:
function insertDefines (shaderCode, defines) {
var defineString = '';
for (var define in defines) {
if (defines.hasOwnProperty(define)) {
defineString +=
'#define ' + define + ' ' + defines[define] + '\n';
}
}
var versionIdx = shaderCode.indexOf('#version');
if (versionIdx == -1) {
return defineString + shaderCode;
}
var nextLineIdx = shaderCode.indexOf('\n', versionIdx) + 1;
return shaderCode.slice(0, nextLineIdx) +
defineString +
shaderCode.slice(nextLineIdx);
}
The implementation is a bit tricky because if the code already has #version preprocessor statement in it, all other statements have to follow it.
Then I've added a check for SCALE_FACROR being defined:
#ifndef SCALE_FACTOR
# error SCALE_FACTOR is undefined
#endif
And in my javascript code I've done something like this:
var SCALE_FACTORS = [4, 8, 16, 32],
shaderCode, // the code of my shader
shaderPrograms = SCALE_FACTORS.map(function (factor) {
var codeWithDefines = insertDefines(shaderCode, { SCALE_FACTOR: factor });
/* compile shaders, link program, return */
});
I use opengl es3 on android and solve this problem by using extension above the beginning of program like this:
#extension GL_EXT_gpu_shader5 : require
I don't know whether it work on webGL, but you can try it.
Hope it can help.
You can also use template litterals to set the length of the loop
onBeforeCompile(shader) {
const array = [1,2,3,4,5];
shader.uniforms.myArray = { value: array };
let token = "#include <begin_vertex>";
const insert = `
uniform float myArray[${array.length}];
for ( int i = 0; i < ${array.length}; i++ ) {
float test = myArray[ i ];
}
`;
shader.vertexShader = shader.vertexShader.replace(token, token + insert);
}

Math performed on CPU has different result on GPU

I am attempting to have a GLSL fragment shader distort incoming fragments based on their texture coordinates to poorly simulate a CRT.
After the code failed to work, I ported it to C++ to modify the RGB values of a texture. The code worked as expected.
This brings me to believe that something is wrong with my GLSL code, even though it is mirrored in C++ and works perfectly.
Is there something that I don't know about GLSL math that could be causing this?
C++ code
const unsigned int RED = 0xFFFF0000;
const unsigned int BLUE = 0xFF0000FF;
const float X_MAX = 429.0f/448.0f;
const float Y_MAX = 320.0f/336.0f;
const float X_CORNER = 410.0f/448.0f;
const float Y_CORNER = 306.0f/336.0f;
const float X_COEF = (X_MAX-X_CORNER) / (Y_CORNER * Y_CORNER);
const float Y_COEF = (Y_MAX-Y_CORNER) / (X_CORNER * X_CORNER);
float FUNCX(float y)
{
return X_MAX-X_COEF*y*y;
}
float FUNCY(float x)
{
return Y_MAX-Y_COEF*x*x;
}
unsigned int get(glm::vec2 intex)
{
intex *= 2.0; // Transform the texture rectangle from 0..1
intex.x -= 1.0; // to
intex.y -= 1.0; // -1 .. 1
glm::vec2 d = glm::vec2(0.0,0.0);
d.x = FUNCX(intex.y); // get the curve amount for X values based on Y input
d.y = FUNCY(intex.x); // get the curve amount for Y values based on X input
if (abs(intex.x/d.x) > 1.0) // if the X value is outside of the curve
return RED; // draw RED for debugging
if (abs(intex.y/d.y) > 1.0) // if the Y value is outside of the curve
return BLUE; // draw BLUE for debugging
glm::vec2 outtex = glm::vec2(0.0f,0.0f);
outtex.x = 1.0 + intex.x/d.x; // Now the -1 .. 1 values get shifted back
outtex.y = 1.0 + intex.y/d.y; // to
outtex /= 2.0; // 0 .. 1
return texture.get(512*outtex.x,512*outtex.y);
}
GLSL fragment shader
const vec4 RED = vec4(1.0,0.0,0.0,1.0);
const vec4 BLUE = vec4(0.0,0.0,1.0,1.0);
const float X_MAX = 429.0/448.0;
const float Y_MAX = 320.0/336.0;
const float X_CORNER = 410.0/448.0;
const float Y_CORNER = 306.0/336.0;
const float X_COEF = (X_MAX-X_CORNER) / (Y_CORNER * Y_CORNER);
const float Y_COEF = (Y_MAX-Y_CORNER) / (X_CORNER * X_CORNER);
float FUNCX(float y)
{
return X_MAX-X_COEF*y*y;
}
float FUNCY(float x)
{
return Y_MAX-Y_COEF*x*x;
}
vec4 get(vec2 intex)
{
intex *= 2.0; // Transform the texture rectangle from 0..1
intex.x -= 1.0; // to
intex.y -= 1.0; // -1 .. 1
vec2 d = vec2(0.0,0.0);
d.x = FUNCX(intex.y); // get the curve amount for X values based on Y input
d.y = FUNCY(intex.x); // get the curve amount for Y values based on X input
if (abs(intex.x/d.x) > 1.0) // if the X value is outside of the curve
return RED; // draw RED for debugging
if (abs(intex.y/d.y) > 1.0) // if the Y value is outside of the curve
return BLUE; // draw BLUE for debugging
vec2 outtex = vec2(0.0,0.0);
outtex.x = 1.0 + intex.x/d.x; // Now the -1 .. 1 values get shifted back
outtex.y = 1.0 + intex.y/d.y; // to
outtex /= 2.0; // 0 .. 1
return texture2D(texture,outtex);
}
Note: The Super Mario World image is for testing purposes only.
Note 2: The 512 values in the C++ code are the size of the texture used.
Edit: There was a typo in the GLSL code where a y value was divided by an x instead of y. This has been fixed and the new output is the same because the denominator values are so close.
The code is not the same. In the C++ code:
if (abs(intex.y/d.y) > 1.0) // if the Y value is outside of the curve
return BLUE; // draw BLUE for debugging
In the GLSL code:
if (abs(intex.y/d.x) > 1.0) // if the Y value is outside of the curve
return BLUE; // draw BLUE for debugging
The C++ version divides by d.y, the GLSL version by d.x.
The formula used only supported UV values from 0 to 1. If values outside of that range were used, the formula broke down.

error X8000 : D3D11 Internal Compiler error : Invalid Bytecode: Invalid operand type for operand #1 of opcode #86 (counts are 1-based)

I'm absolutely stumped as well as my instructors/lab-assistants.
For some reason, the following HLSL code is returning this in the output window:
error X8000 : D3D11 Internal Compiler error : Invalid Bytecode: Invalid operand type for operand #1 of opcode #86 (counts are 1-based).
Here's the function in the HLSL causing the issue:
// Projects a sphere diameter large in screen space to calculate desired tesselation factor
float SphereToScreenSpaceTessellation(float3 p0, float3 p1, float diameter)
{
float3 centerPoint = (p0 + p1) * 0.5f;
float4 point0 = mul( float4(centerPoint,1.0f) , gTileWorldView);
float4 point1 = point0;
point1.x += diameter;
float4 point0ClipSpace = mul(point0, gTileProj);
float4 point1ClipSpace = mul(point1, gTileProj);
point0ClipSpace /= point0ClipSpace.w;
point1ClipSpace /= point1ClipSpace.w;
point0ClipSpace.xy *= gScreenSize;
point1ClipSpace.xy *= gScreenSize;
float projSizeOfEdge = distance(point0ClipSpace, point1ClipSpace);
float result = projSizeOfEdge / gTessellatedTriWidth;
return clamp(result, 0, 64);
}
I've narrowed it down to the point where it may be the "mul" intrinsic. We've taken everything out of the code and tried to return out a temporary variable like this, and it works fine:
float SphereToScreenSpaceTessellation(float3 p0, float3 p1, float diameter)
{
float temp = 0;
float3 centerPoint = (p0 + p1) * 0.5f;
float4 point0 = mul( float4(centerPoint,1.0f) , gTileWorldView);
float4 point1 = point0;
point1.x += diameter;
float4 point0ClipSpace = mul(point0, gTileProj);
float4 point1ClipSpace = mul(point1, gTileProj);
point0ClipSpace /= point0ClipSpace.w;
point1ClipSpace /= point1ClipSpace.w;
point0ClipSpace.xy *= gScreenSize;
point1ClipSpace.xy *= gScreenSize;
float projSizeOfEdge = distance(point0ClipSpace, point1ClipSpace);
float result = projSizeOfEdge / gTessellatedTriWidth;
return temp;
//return clamp(result, 0, 64);
}
If anyone is wondering:
gTileWorldView, gTileProj are float4x4's in a .hlsli file
gScreenSize is a float2 in a .hlsli file.
gTessellatedTriWidth is a float in a .hlsli file.
The following function is as states in a 2011 NVidia shader at : http://dx11-xpr.googlecode.com/svn/trunk/XPR/Media/Effects/TerrainTessellation.fx
I tried to copy and paste their solution replacing their variables with the one above, and the same error listed happens.
I'm absolutely stumped and I need assistance in order to do this assignment, please help.
Check out this line:
point0ClipSpace.xy *= gScreenSize;
Is gScreenSize a float2? I do not believe you can scalar multiply a vec by any vec type.

Converting 2D Noise to 3D

I've recently started experimenting with noise (simple perlin noise), and have run into a slight problem with animating it. So far come I've across an awesome looking 3d noise (https://github.com/ashima/webgl-noise) that I could use in my project but that I understood nothing of, and a bunch of tutorials that explain how to create simple 2d noise.
For the 2d noise, I originally used the following fragment shader:
uniform sampler2D al_tex;
varying vec4 varying_pos; //Actual coords
varying vec2 varying_texcoord; //Normalized coords
uniform float time;
float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); }
float ease(float p) { return 3*p*p - 2*p*p*p; }
float cnoise(vec2 p, int wavelength)
{
int ix1 = (int(varying_pos.x) / wavelength) * wavelength;
int iy1 = (int(varying_pos.y) / wavelength) * wavelength;
int ix2 = (int(varying_pos.x) / wavelength) * wavelength + wavelength;
int iy2 = (int(varying_pos.y) / wavelength) * wavelength + wavelength;
float x1 = ix1 / 1280.0f;
float y1 = iy1 / 720.0f;
float x2 = ix2 / 1280.0f;
float y2 = iy2 / 720.0f;
float xOffset = (varying_pos.x - ix1) / wavelength;
float yOffset = (varying_pos.y - iy1) / wavelength;
xOffset = ease(xOffset);
yOffset = ease(yOffset);
float t1 = rand(vec2(x1, y1));
float t2 = rand(vec2(x2, y1));
float t3 = rand(vec2(x2, y2));
float t4 = rand(vec2(x1, y2));
float tt1 = mix(t1, t2, xOffset);
float tt2 = mix(t4, t3, xOffset);
return mix(tt1, tt2, yOffset);
}
void main()
{
float t = 0;
int minFreq = 0;
int noIterations = 8;
for (int i = 0; i < noIterations; i++)
t += cnoise(varying_texcoord, int(pow(2, i + minFreq))) / pow(2, noIterations - i);
gl_FragColor = vec4(vec3(t), 1);
}
The result that I got was this:
Now, I want to animate it with time. My first thought was to change the rand function to take a vec3 instead of vec2, and then change my cnoise function accordingly, to interpolate values in the z direction too. With that goal in mind, I made this:
sampler2D al_tex;
varying vec4 varying_pos;
varying vec2 varying_texcoord;
uniform float time;
float rand(vec3 co) { return fract(sin(dot(co, vec3(12.9898, 78.2332, 58.5065))) * 43758.5453); }
float ease(float p) { return 3*p*p - 2*p*p*p; }
float cnoise(vec3 pos, int wavelength)
{
ivec3 iPos1 = (ivec3(pos) / wavelength) * wavelength; //The first value that I'll sample to interpolate
ivec3 iPos2 = iPos1 + wavelength; //The second value
vec3 transPercent = (pos - iPos1) / wavelength; //Transition percent - A float in [0-1) indicating how much of each of the above values will contribute to final result
transPercent.x = ease(transPercent.x);
transPercent.y = ease(transPercent.y);
transPercent.z = ease(transPercent.z);
float t1 = rand(vec3(iPos1.x, iPos1.y, iPos1.z));
float t2 = rand(vec3(iPos2.x, iPos1.y, iPos1.z));
float t3 = rand(vec3(iPos2.x, iPos2.y, iPos1.z));
float t4 = rand(vec3(iPos1.x, iPos2.y, iPos1.z));
float t5 = rand(vec3(iPos1.x, iPos1.y, iPos2.z));
float t6 = rand(vec3(iPos2.x, iPos1.y, iPos2.z));
float t7 = rand(vec3(iPos2.x, iPos2.y, iPos2.z));
float t8 = rand(vec3(iPos1.x, iPos2.y, iPos2.z));
float tt1 = mix(t1, t2, transPercent.x);
float tt2 = mix(t4, t3, transPercent.x);
float tt3 = mix(t5, t6, transPercent.x);
float tt4 = mix(t8, t7, transPercent.x);
float tt5 = mix(tt1, tt2, transPercent.y);
float tt6 = mix(tt3, tt4, transPercent.y);
return mix(tt5, tt6, transPercent.z);
}
float fbm(vec3 p)
{
float t = 0;
int noIterations = 8;
for (int i = 0; i < noIterations; i++)
t += cnoise(p, int(pow(2, i))) / pow(2, noIterations - i);
return t;
}
void main()
{
vec3 p = vec3(varying_pos.xy, time);
float t = fbm(p);
gl_FragColor = vec4(vec3(t), 1);
}
However, on doing this, the animation feels... strange. It's as though I'm watching a slideshow of perlin noise slides, with the individual slides fading in. All other perlin noise examples that I have tried (like https://github.com/ashima/webgl-noise) are actually animated with time - you can actually see it being animated, and don't just feel like the images are fading in, and not being actually animated. I know that I could just use the webgl-noise shader, but I want to make one for myself, and for some reason, I'm failing miserably. Could anyone tell me where I am going wrong, or suggest me on how I can actually animate it properly with time?
You should proably include z in the sin function:
float rand(vec3 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233)) + co.z) * 43758.5453); }
Apparently the somewhat random numbers are prime numbers. This is to avoid patterns in the noise. I found another prime number, 94418953, and included that in the sin/dot function. Try this:
float rand(vec3 co) { return fract(sin(dot(co.xyz ,vec3(12.9898,78.233, 9441.8953))) * 43758.5453); }
EDIT: You don't take into account wavelength on the z axis. This means that all your iterations will have the same interpolation distance. In other words, you will get the fade effect you're describing. Try calculating z the same way you calculate x and y:
int iz1 = (int(p.z) / wavelength) * wavelength;
int iz2 = (int(p.z) / wavelength) * wavelength + wavelength;
float z1 = iz1 / 720.0f;
float z2 = iz2 / 720.0f;
float zOffset = (varying_pos.z - iz1) / wavelength;
This means however that the z value will variate the same rate that y will. So if you want it to scale from 0 to 1 then you should proably multiply z with 720 before passing it into the noise function.
check this code. it's a simple version of 3d noise:
// Here are some easy to understand noise gens... the D line in cubic interpolation (rounding)
function rndng ( n: float ): float
{//random proportion -1, 1 ... many people use Sin to take
//linearity out of a pseudo random, exp n*n is faster on central processor.
var e = ( n *321.9234)%1;
return (e*e*111.07546)%2-1;
}
function lerps(o:float, v:float, alpha:float):float
{
o += ( v - o ) * alpha;
return o;
}
//3d ----------------
function lnz ( vtx: Vector3 ): float //3d perlin noise code fast
{
vtx= Vector3 ( Mathf.Abs(vtx.x) , Mathf.Abs(vtx.y) , Mathf.Abs(vtx.z) ) ;
var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
var W = I.x + I.y*71.0 + 125.0*I.z;
return lerps(
lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.y)
,
lerps( lerps(rndng(W+125.0),rndng(W+126.0),D.x) , lerps(rndng(W+153.0),rndng(W+154.0),D.x) , D.y)
,
D.z
);
}
//1d ----------------
function lnzo ( vtx: Vector3 ): float //perlin noise, same as unityfunction version
{
var total = 0.0;
for (var i:int = 1; i < 5; i ++)
{
total+= lnz2(Vector3 (vtx.x*(i*i),0.0,vtx.z*(i*i)))/(i*i);
}
return total*5;
}
//2d 3 axis honeycombe noise ----------------
function lnzh ( vtx: Vector3 ): float // perlin noise, 2d, with 3 axes at 60'instead of 2 x y axes
{
vtx= Vector3 ( Mathf.Abs(vtx.z) , Mathf.Abs(vtx.z*.5-vtx.x*.866) , Mathf.Abs(vtx.z*.5+vtx.x*.866) ) ;
var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
//D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
var W = I.x + I.y*71.0 + 125.0*I.z;
return lerps(
lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.y)
,
lerps( lerps(rndng(W+125.0),rndng(W+126.0),D.x) , lerps(rndng(W+153.0),rndng(W+154.0),D.x) , D.y)
,
D.z
);
}
//2d ----------------
function lnz2 ( vtx: Vector3 ): float // i think this is 2d perlin noise
{
vtx= Vector3 ( Mathf.Abs(vtx.x) , Mathf.Abs(vtx.y) , Mathf.Abs(vtx.z) ) ;
var I = Vector3 (Mathf.Floor(vtx.x),Mathf.Floor(vtx.y),Mathf.Floor(vtx.z));
var D = Vector3(vtx.x%1,vtx.y%1,vtx.z%1);
D = Vector3(D.x*D.x*(3.0-2.0*D.x),D.y*D.y*(3.0-2.0*D.y),D.z*D.z*(3.0-2.0*D.z));
var W = I.x + I.y*71.0 + 125.0*I.z;
return lerps(
lerps( lerps(rndng(W+0.0),rndng(W+1.0),D.x) , lerps(rndng(W+71.0),rndng(W+72.0),D.x) , D.z)
,
lerps( rndng(W+125.0), rndng(W+126.0),D.x)
,
D.z
);
}