import type { FloatSym , Vec2Sym , Vec3Sym } from "@thi.ng/shader-ast" ; import { GLSLVersion , targetGLSL } from "@thi.ng/shader-ast-glsl" ; import { canvasRenderer } from "@thi.ng/shader-ast-js/runtime" ; import { targetJS } from "@thi.ng/shader-ast-js/target" ; import { fogExp2 } from "@thi.ng/shader-ast-stdlib/fog/exp2" ; import { diffuseLighting , halfLambert , } from "@thi.ng/shader-ast-stdlib/light/lambert" ; import { clamp01 } from "@thi.ng/shader-ast-stdlib/math/clamp" ; import { fit1101 } from "@thi.ng/shader-ast-stdlib/math/fit" ; import { lookat } from "@thi.ng/shader-ast-stdlib/matrix/lookat" ; import { raymarchAO } from "@thi.ng/shader-ast-stdlib/raymarch/ao" ; import { raymarchDir } from "@thi.ng/shader-ast-stdlib/raymarch/direction" ; import { raymarchNormal } from "@thi.ng/shader-ast-stdlib/raymarch/normal" ; import { rayPointAt } from "@thi.ng/shader-ast-stdlib/raymarch/point-at" ; import { raymarchScene } from "@thi.ng/shader-ast-stdlib/raymarch/scene" ; import { sdfBox3 } from "@thi.ng/shader-ast-stdlib/sdf/box" ; import { sdfRepeat3 } from "@thi.ng/shader-ast-stdlib/sdf/repeat" ; import { sdfSmoothUnion } from "@thi.ng/shader-ast-stdlib/sdf/smooth-union" ; import { sdfSphere } from "@thi.ng/shader-ast-stdlib/sdf/sphere" ; import { assign } from "@thi.ng/shader-ast/ast/assign" ; import { ifThen } from "@thi.ng/shader-ast/ast/controlflow" ; import { defMain , defn , ret } from "@thi.ng/shader-ast/ast/function" ; import { float , vec2 , vec3 , vec4 } from "@thi.ng/shader-ast/ast/lit" ; import { gte , mul } from "@thi.ng/shader-ast/ast/ops" ; import { program } from "@thi.ng/shader-ast/ast/scope" ; import { $x , $xy , $xyz } from "@thi.ng/shader-ast/ast/swizzle" ; import { sym } from "@thi.ng/shader-ast/ast/sym" ; import { mix } from "@thi.ng/shader-ast/builtin/math" ; import type { GLVec3 } from "@thi.ng/webgl" ; import { compileModel } from "@thi.ng/webgl/buffer" ; import { draw } from "@thi.ng/webgl/draw" ; import { defQuadModel } from "@thi.ng/webgl/geo/quad" ; import { defShader } from "@thi.ng/webgl/shader" ; const JS_MODE = location . hash . indexOf ( "2d" ) >= 0 ; const GL = targetGLSL ( { version : GLSLVersion . GLES_100 , versionPragma : false } ) ; const JS = targetJS ( ) ; const scene = defn ( "vec2" , "scene" , [ "vec3" ] , ( pos ) => { let d1 ; let d2 ; let d3 ; let d4 ; return [ assign ( pos , sdfRepeat3 ( pos , vec3 ( 2.1 ) ) ) , ( d1 = sym ( sdfSphere ( pos , float ( 0.5 ) ) ) ) , ( d2 = sym ( sdfBox3 ( pos , vec3 ( 1 , 0.2 , 0.2 ) ) ) ) , ( d3 = sym ( sdfBox3 ( pos , vec3 ( 0.2 , 0.2 , 1 ) ) ) ) , ( d4 = sym ( sdfBox3 ( pos , vec3 ( 0.2 , 1 , 0.2 ) ) ) ) , ret ( vec2 ( sdfSmoothUnion ( sdfSmoothUnion ( sdfSmoothUnion ( d1 , d2 , float ( 0.2 ) ) , d3 , float ( 0.2 ) ) , d4 , float ( 0.2 ) ) , 1 ) ) , ] ; } ) ; const mainImage = defn ( "vec4" , "mainImage" , [ "vec2" , "vec2" , "vec3" , "vec3" ] , ( frag , res , eyePos , lightDir ) => { let dir ; let result ; let isec ; let norm ; let material ; let diffuse ; const bg = vec3 ( 1.5 , 0.6 , 0 ) ; const ambient = vec3 ( 0.15 , 0.06 , 0 ) ; return [ ( dir = sym ( $xyz ( mul ( lookat ( eyePos , vec3 ( ) , vec3 ( 0 , 1 , 0 ) ) , vec4 ( raymarchDir ( frag , res , float ( 120 ) ) , 0 ) ) ) ) ) , ( result = sym ( raymarchScene ( scene , { steps : JS_MODE ? 60 : 80 , eps : 0.005 } ) ( eyePos , dir ) ) ) , ifThen ( gte ( $x ( result ) , float ( 10 ) ) , [ ret ( vec4 ( bg , 1 ) ) ] ) , ( isec = sym ( rayPointAt ( eyePos , dir , $x ( result ) ) ) ) , ( norm = sym ( raymarchNormal ( scene ) ( isec , float ( 0.01 ) ) ) ) , ( material = sym ( fit1101 ( isec ) ) ) , ( diffuse = sym ( mul ( halfLambert ( norm , lightDir ) , raymarchAO ( scene ) ( isec , norm ) ) ) ) , ret ( vec4 ( mix ( clamp01 ( diffuseLighting ( diffuse , material , vec3 ( 1 ) , ambient ) ) , bg , fogExp2 ( $x ( result ) , float ( 0.2 ) ) ) , 1 ) ) , ] ; } ) ; const shaderProgram = program ( [ mainImage ] ) ; console . log ( "JS" ) ; console . log ( JS ( shaderProgram ) ) ; console . log ( "GLSL" ) ; console . log ( GL ( shaderProgram ) ) ; const W = 320 ; const H = 240 ; const size = [ W , H ] ; const canvas = document . createElement ( "canvas" ) ; canvas . width = W ; canvas . height = H ; document . body . appendChild ( canvas ) ; const info = document . createElement ( "div" ) ; info . innerText = ( JS_MODE ? "Canvas2D" : "WebGL" ) + " version" ; document . body . appendChild ( info ) ; const lightDir = [ 0.707 , 0.707 , 0 ] ; if ( JS_MODE ) { const fn = JS . compile ( shaderProgram ) . mainImage ; const rt = canvasRenderer ( canvas ) ; let time = 0 ; setInterval ( ( ) => { time += 0.1 ; const eyePos = [ Math . cos ( time ) * 2.5 , Math . cos ( time / 2 ) * 0.7 , Math . sin ( time ) * 2.5 , ] ; rt ( ( frag ) => fn ( frag , size , eyePos , lightDir ) ) ; } , 16 ) ; } else { const ctx = canvas . getContext ( "webgl" ) ! ; const model = defQuadModel ( { uv : false } ) ; model . shader = defShader ( ctx , { vs : ( _ , __ , attribs ) => [ defMain ( ( ) => [ assign ( GL . gl_Position , vec4 ( attribs . position , 0 , 1 ) ) , ] ) , ] , fs : ( gl , unis , _ , outputs ) => [ mainImage , defMain ( ( ) => [ assign ( outputs . fragColor , mainImage ( $xy ( gl . gl_FragCoord ) , unis . resolution , unis . eyePos , unis . lightDir ) ) , ] ) , ] , attribs : { position : "vec2" , } , uniforms : { eyePos : "vec3" , lightDir : [ "vec3" , < GLVec3 > lightDir ] , resolution : [ "vec2" , [ W , H ] ] , } , } ) ; compileModel ( ctx , model ) ; const t0 = Date . now ( ) ; setInterval ( ( ) => { const time = ( Date . now ( ) - t0 ) * 0.001 ; model . uniforms ! . eyePos = [ Math . cos ( time ) * 2.5 , Math . cos ( time / 2 ) * 0.7 , Math . sin ( time ) * 2.5 , ] ; draw ( model ) ; } ) ; }