Newer
Older
kotlin-mandelbrot / src / main / kotlin / Julia.kt
rnentjes on 25 Jul 2022 5 KB Upgrade to latest kotlin with gradle
import org.khronos.webgl.Float32Array
import org.khronos.webgl.WebGLBuffer
import org.khronos.webgl.WebGLRenderingContext
import kotlinx.browser.window
import kotlin.js.Date
import kotlin.math.cos
import kotlin.math.sin

/**
 * User: rnentjes
 * Date: 21-5-16
 * Time: 17:06
 */

private val vertexShader = """
    attribute vec2 a_position;

    uniform vec4 u_viewWindow;
    uniform float u_aspect;

    varying vec2 v_coord;

    void main(void) {
        v_coord = vec2(a_position.x / u_aspect, a_position.y * u_aspect) * u_viewWindow.zw + u_viewWindow.xy;

        gl_Position = vec4(a_position, 0.0, 1.0);
    }
"""

private val fragmentShader = """
    precision mediump float;

    uniform vec2 u_julia;
    uniform int u_max_iterations;
    uniform float u_iteratorOffset;

    varying vec2 v_coord;

    vec3 hsv2rgb(vec3 c) {
        vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
        vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
        return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
    }
    
    void main(void) {
        float xx = v_coord.x;
        float yy = v_coord.y;
        float xt = 0.0;
        float sc = xx*xx + yy*yy;

        gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0);

        if (sc < 4.0) {
            for (int iteration = 0; iteration < 1000; iteration++) {
                sc = xx*xx + yy*yy;
                if (sc > 4.0) {
                  float mu = float(iteration) + 1.0 - log(log(sc)) / log(2.0);
                  //mu = sqrt(mu);

                  float ci = sc / float(u_max_iterations);
                  
                  vec3 hsl = vec3(mod(u_iteratorOffset + mu * 13.0, float(u_max_iterations)) / float(u_max_iterations), 1.0, 0.75);
                  vec3 rgb = hsv2rgb(hsl);
                  
                  //float it = mod(mu * 23.0, 768.0);

                  //float red = min(it, 255.0) / 255.0;
                  //float green = max(0.0, min(it, 511.0) - 256.0) / 255.0;
                  //float blue = max(0.0, min(it, 767.0) - 512.0) / 255.0;

                  //gl_FragColor = vec4( blue, green, red, 1.0);
                  gl_FragColor = vec4( rgb, 1.0);
                  break;
                }
                xt = xx*xx - yy*yy + u_julia.x;
                yy = 2.0*xx*yy + u_julia.y;
                xx = xt;
            }
        }
    }
"""

class JuliaData {
    var juliaX: Float = 0f
    var juliaY: Float = 0f

    var offsetX: Float = 0f
    var offsetY: Float = 0f
    var scaleX: Float = 1f
    var scaleY: Float = 1f

    var aspect: Float = 1f

    var max_iterations: Int = 25
    var iteratorOffset: Float = 0f
}

class Julia(val html: HTMLElements) {
    val webgl = html.webgl
    val shaderProgram: ShaderProgram<JuliaData>
    val data: JuliaData = JuliaData()
    val attribBuffer: WebGLBuffer
    val vertices: Float32Array
    val start = Date().getTime()

    init {
        val array: Array<Float> = arrayOf(
          -1f,-1f,
           1f,-1f,
           1f, 1f,
           1f, 1f,
          -1f, 1f,
          -1f,-1f
        )

        vertices = Float32Array(array.size)
        vertices.set(array, 0)

        val setter = { program: ShaderProgram<JuliaData>, data: JuliaData ->
            program.setUniform2f("u_julia", data.juliaX, data.juliaY)
            program.setUniform4f("u_viewWindow", data.offsetX, data.offsetY, data.scaleX, data.scaleY)
            program.setUniform1f("u_iteratorOffset", data.iteratorOffset)
            program.setUniform1f("u_aspect", data.aspect)
            program.setUniform1i("u_max_iterations", data.max_iterations)
        }

        val vainfo = arrayOf(
              VertextAttributeInfo("a_position", 2),
        )

        shaderProgram = ShaderProgram(webgl, WebGLRenderingContext.TRIANGLES, vertexShader, fragmentShader, vainfo, setter)

        attribBuffer = webgl.createBuffer() ?: throw IllegalStateException("Unable to create webgl buffer!")
        webgl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, attribBuffer);
    }

    fun render() {
        html.resize()

        //webgl.clearColor(1f, 1f, 1f, 1f)
        //webgl.clear(WebGLRenderingContext.COLOR_BUFFER_BIT)

        val time = (start - (Date().getTime())) / 500.0

//        data.juliaX = -0.391f + (Math.sin(time / 31) / 10f).toFloat()
//        data.juliaY = -0.587f + (Math.cos(time / 23.07) / 10f).toFloat()

//        data.juliaX = -0.79f + (Math.sin(time / 31) / 100f).toFloat()
//        data.juliaY = 0.15f + (Math.cos(time / 23.07) / 100f).toFloat()

        data.juliaX = 0f + (sin(time * 1 / 11) / 1f).toFloat()
        data.juliaY = 0f + (cos(time * 1 / 13) / 1f).toFloat()

        //data.scaleX = 1.3f - sin(time / 10.0).toFloat() * 0.5f
        //data.scaleY = 1.3f - sin(time / 10.0).toFloat() * 0.5f

        data.scaleX = 1f
        data.scaleY = 1f / (html.windowWidth.toFloat() / html.windowHeight.toFloat())

        data.iteratorOffset = time.toFloat() * 10f

        data.aspect = 1f //html.windowHeight.toFloat() / html.windowWidth.toFloat() * 2f
        data.max_iterations = 360

        shaderProgram.begin(attribBuffer, data)

        webgl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, vertices, WebGLRenderingContext.DYNAMIC_DRAW);
        webgl.drawArrays(shaderProgram.drawType, 0, 6)

        shaderProgram.end()

        window.requestAnimationFrame {
            render()
        }
    }
}