diff --git a/Content/hexagon_grid.spv b/Content/hexagon_grid.spv index b05224c..a9ddd45 100644 Binary files a/Content/hexagon_grid.spv and b/Content/hexagon_grid.spv differ diff --git a/lib/MoonWorks b/lib/MoonWorks index d2fca36..111df04 160000 --- a/lib/MoonWorks +++ b/lib/MoonWorks @@ -1 +1 @@ -Subproject commit d2fca3654bd90817885f0006058cccd6437da22c +Subproject commit 111df04c0f7be740108cc3536eda3629572714d8 diff --git a/moonlibs/windows/Refresh.dll b/moonlibs/windows/Refresh.dll index 2d0c008..09ca122 100644 Binary files a/moonlibs/windows/Refresh.dll and b/moonlibs/windows/Refresh.dll differ diff --git a/src/Vertex.cs b/src/PositionTexture.cs similarity index 51% rename from src/Vertex.cs rename to src/PositionTexture.cs index ea9731c..b594038 100644 --- a/src/Vertex.cs +++ b/src/PositionTexture.cs @@ -1,11 +1,12 @@ using System.Runtime.InteropServices; +using MoonWorks.Math; namespace MoonWorksTest { [StructLayout(LayoutKind.Sequential)] - struct Vertex + struct PositionTexture { - public float x, y, z; - public float u, v; + public Vector3 Position; + public Vector2 Texture; } } diff --git a/src/Program.cs b/src/Program.cs index ca86e2b..856cacb 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,5 +1,5 @@ +using MoonWorks; using MoonWorks.Graphics; -using MoonWorks.Window; namespace MoonWorksTest { @@ -15,7 +15,13 @@ namespace MoonWorksTest ScreenMode = ScreenMode.Windowed }; - TestGame game = new TestGame(windowCreateInfo, PresentMode.FIFO, 60, true); + TestGame game = new TestGame( + windowCreateInfo, + PresentMode.FIFO, + 60, + true + ); + game.Run(); } } diff --git a/src/TestGame.cs b/src/TestGame.cs index a78de0b..2b7d3e8 100644 --- a/src/TestGame.cs +++ b/src/TestGame.cs @@ -2,9 +2,8 @@ using MoonWorks; using MoonWorks.Audio; using MoonWorks.Graphics; using MoonWorks.Input; -using MoonWorks.Window; using System.IO; -using System.Threading; +using System.Threading.Tasks; namespace MoonWorksTest { @@ -20,28 +19,20 @@ namespace MoonWorksTest Buffer vertexBuffer; - Rect renderArea; - Rect flip; Color clearColor; - DepthStencilValue depthStencilClear; - Texture mainColorTargetTexture; - TextureSlice mainColorTargetTextureSlice; - RenderTarget mainColorTarget; - - RenderPass mainRenderPass; - Framebuffer mainFramebuffer; GraphicsPipeline mainGraphicsPipeline; byte[] screenshotPixels; Buffer screenshotBuffer; - uint screenShotBufferSize; - Thread screenshotThread; + uint screenshotBufferSize; StaticSound music; StaticSoundInstance musicInstance; StreamingSoundOgg musicStream; + bool screenshotInProgress = false; + public TestGame(WindowCreateInfo windowCreateInfo, PresentMode presentMode, int targetTimestep = 60, bool debugMode = false) : base(windowCreateInfo, presentMode, targetTimestep, debugMode) { var windowWidth = windowCreateInfo.WindowWidth; @@ -64,206 +55,60 @@ namespace MoonWorksTest /* Load Vertex Data */ - var vertices = new Vertex[3]; - vertices[0].x = -1; - vertices[0].y = -1; - vertices[0].z = 0; - vertices[0].u = 0; - vertices[0].v = 1; + var vertices = new PositionTexture[3]; + vertices[0].Position.X = -1; + vertices[0].Position.Y = -1; + vertices[0].Position.Z = 0; + vertices[0].Texture.X = 0; + vertices[0].Texture.Y = 1; - vertices[1].x = 3; - vertices[1].y = -1; - vertices[1].z = 0; - vertices[1].u = 1; - vertices[1].v = 1; + vertices[1].Position.X = 3; + vertices[1].Position.Y = -1; + vertices[1].Position.Z = 0; + vertices[1].Texture.X = 1; + vertices[1].Texture.Y = 1; - vertices[2].x = -1; - vertices[2].y = 3; - vertices[2].z = 0; - vertices[2].u = 0; - vertices[2].v = 0; + vertices[2].Position.X = -1; + vertices[2].Position.Y = 3; + vertices[2].Position.Z = 0; + vertices[2].Texture.X = 0; + vertices[2].Texture.Y = 0; - vertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, 4 * 5 * 3); + vertexBuffer = Buffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, 3); uploadCommandBuffer.SetBufferData(vertexBuffer, vertices); GraphicsDevice.Submit(uploadCommandBuffer); - /* Render Pass */ - - renderArea.X = 0; - renderArea.Y = 0; - renderArea.W = (int) windowWidth; - renderArea.H = (int) windowHeight; - - flip.X = 0; - flip.Y = (int) windowHeight; - flip.W = (int) windowWidth; - flip.H = -(int) windowHeight; - - clearColor.R = 237; - clearColor.G = 41; - clearColor.B = 57; - clearColor.A = byte.MaxValue; - - ColorTargetDescription colorTargetDescription = new ColorTargetDescription - { - Format = TextureFormat.R8G8B8A8, - MultisampleCount = SampleCount.One, - LoadOp = LoadOp.Clear, - StoreOp = StoreOp.Store - }; - - mainRenderPass = new RenderPass(GraphicsDevice, colorTargetDescription); - - mainColorTargetTexture = Texture.CreateTexture2D( - GraphicsDevice, - windowWidth, - windowHeight, - TextureFormat.R8G8B8A8, - TextureUsageFlags.ColorTarget - ); - - mainColorTargetTextureSlice = new TextureSlice(mainColorTargetTexture); - mainColorTarget = new RenderTarget(GraphicsDevice, mainColorTargetTextureSlice); - - mainFramebuffer = new Framebuffer( - GraphicsDevice, - windowWidth, - windowHeight, - mainRenderPass, - null, - mainColorTarget - ); - /* Pipeline */ - ColorTargetBlendState[] colorTargetBlendStates = new ColorTargetBlendState[1] - { - ColorTargetBlendState.None - }; - - ColorBlendState colorBlendState = new ColorBlendState - { - LogicOpEnable = false, - LogicOp = LogicOp.NoOp, - BlendConstants = new BlendConstants(), - ColorTargetBlendStates = colorTargetBlendStates - }; - - DepthStencilState depthStencilState = DepthStencilState.Disable; - - ShaderStageState vertexShaderState = new ShaderStageState - { - ShaderModule = passthroughVertexShaderModule, - EntryPointName = "main", - UniformBufferSize = 0 - }; - - ShaderStageState fragmentShaderState = new ShaderStageState - { - ShaderModule = raymarchFragmentShaderModule, - EntryPointName = "main", - UniformBufferSize = 16 - }; - - MultisampleState multisampleState = MultisampleState.None; - - GraphicsPipelineLayoutInfo pipelineLayoutInfo = new GraphicsPipelineLayoutInfo - { - VertexSamplerBindingCount = 0, - FragmentSamplerBindingCount = 2 - }; - - RasterizerState rasterizerState = RasterizerState.CW_CullBack; - - var vertexBindings = new VertexBinding[1] - { - new VertexBinding - { - Binding = 0, - InputRate = VertexInputRate.Vertex, - Stride = 4 * 5 - } - }; - - var vertexAttributes = new VertexAttribute[2] - { - new VertexAttribute - { - Binding = 0, - Location = 0, - Format = VertexElementFormat.Vector3, - Offset = 0 - }, - new VertexAttribute - { - Binding = 0, - Location = 1, - Format = VertexElementFormat.Vector2, - Offset = 4 * 3 - } - }; - - VertexInputState vertexInputState = new VertexInputState - { - VertexBindings = vertexBindings, - VertexAttributes = vertexAttributes - }; - - var viewports = new Viewport[1] - { - new Viewport - { - X = 0, - Y = 0, - W = windowWidth, - H = windowHeight, - MinDepth = 0, - MaxDepth = 1 - } - }; - - var scissors = new Rect[1] - { - new Rect - { - X = 0, - Y = 0, - W = (int) windowWidth, - H = (int) windowHeight - } - }; - - ViewportState viewportState = new ViewportState - { - Viewports = viewports, - Scissors = scissors - }; - - var graphicsPipelineCreateInfo = new GraphicsPipelineCreateInfo - { - ColorBlendState = colorBlendState, - DepthStencilState = depthStencilState, - VertexShaderState = vertexShaderState, - FragmentShaderState = fragmentShaderState, - MultisampleState = multisampleState, - PipelineLayoutInfo = pipelineLayoutInfo, - RasterizerState = rasterizerState, - PrimitiveType = PrimitiveType.TriangleList, - VertexInputState = vertexInputState, - ViewportState = viewportState, - RenderPass = mainRenderPass - }; - mainGraphicsPipeline = new GraphicsPipeline( GraphicsDevice, - graphicsPipelineCreateInfo + new GraphicsPipelineCreateInfo + { + AttachmentInfo = new GraphicsPipelineAttachmentInfo( + new ColorAttachmentDescription( + GraphicsDevice.GetSwapchainFormat(Window), + ColorAttachmentBlendState.None + ) + ), + DepthStencilState = DepthStencilState.Disable, + VertexShaderInfo = GraphicsShaderInfo.Create(passthroughVertexShaderModule, "main", 0), + VertexInputState = new VertexInputState( + VertexBinding.Create(), + VertexAttribute.Create("Position", 0), + VertexAttribute.Create("Texture", 1) + ), + PrimitiveType = PrimitiveType.TriangleList, + FragmentShaderInfo = GraphicsShaderInfo.Create(raymarchFragmentShaderModule, "main", 2), + RasterizerState = RasterizerState.CW_CullBack, + ViewportState = new ViewportState((int)Window.Width, (int)Window.Height), + MultisampleState = MultisampleState.None + } ); - screenShotBufferSize = windowWidth * windowHeight * 4; - screenshotPixels = new byte[screenShotBufferSize]; - screenshotBuffer = new Buffer(GraphicsDevice, 0, screenShotBufferSize); - screenshotThread = new Thread(new ThreadStart(SaveScreenshot)); + screenshotBufferSize = windowWidth * windowHeight * 4; + screenshotPixels = new byte[screenshotBufferSize]; + screenshotBuffer = new Buffer(GraphicsDevice, 0, screenshotBufferSize); music = StaticSound.LoadOgg(AudioDevice, Path.Combine("Content", "title_screen.ogg")); musicInstance = music.CreateInstance(); @@ -280,49 +125,69 @@ namespace MoonWorksTest protected override void Draw(System.TimeSpan dt, double alpha) { - var screenshotPressed = Inputs.Keyboard.IsPressed(Keycode.S); - var commandBuffer = GraphicsDevice.AcquireCommandBuffer(); - commandBuffer.BeginRenderPass( - mainRenderPass, - mainFramebuffer, - renderArea, - depthStencilClear, - clearColor.ToVector4() - ); + var swapchainTexture = commandBuffer.AcquireSwapchainTexture(Window); + var takeScreenshot = Inputs.Keyboard.IsPressed(Keycode.S) && !screenshotInProgress && (swapchainTexture != null); - commandBuffer.BindGraphicsPipeline(mainGraphicsPipeline); - - commandBuffer.BindVertexBuffers(0, new BufferBinding(vertexBuffer, 0)); - commandBuffer.BindFragmentSamplers( - new TextureSamplerBinding(woodTexture, sampler), - new TextureSamplerBinding(noiseTexture, sampler) - ); - - var fragmentParamOffset = commandBuffer.PushFragmentShaderUniforms(raymarchUniforms); - commandBuffer.DrawPrimitives(0, 1, 0, fragmentParamOffset); - commandBuffer.EndRenderPass(); - - if (screenshotPressed) + if (swapchainTexture != null) { - commandBuffer.CopyTextureToBuffer(mainColorTargetTextureSlice, screenshotBuffer); + commandBuffer.BeginRenderPass( + new ColorAttachmentInfo(swapchainTexture, clearColor) + ); + + commandBuffer.BindGraphicsPipeline(mainGraphicsPipeline); + + commandBuffer.BindVertexBuffers(vertexBuffer); + commandBuffer.BindFragmentSamplers( + new TextureSamplerBinding(woodTexture, sampler), + new TextureSamplerBinding(noiseTexture, sampler) + ); + + var fragmentParamOffset = commandBuffer.PushFragmentShaderUniforms(raymarchUniforms); + commandBuffer.DrawPrimitives(0, 1, 0, fragmentParamOffset); + commandBuffer.EndRenderPass(); + + if (takeScreenshot) + { + commandBuffer.CopyTextureToBuffer(new TextureSlice(swapchainTexture), screenshotBuffer); + } } - commandBuffer.QueuePresent(mainColorTargetTextureSlice, flip, Filter.Nearest); GraphicsDevice.Submit(commandBuffer); - if (screenshotPressed) + if (takeScreenshot) { - screenshotThread.Start(); + Task.Run(() => SaveScreenshot()); } } private void SaveScreenshot() { + screenshotInProgress = true; + + var name = "MoonWorksTest-" + System.DateTime.Now.ToString("MM-dd-yyyy-hh-mm-ss") + ".png"; + System.Console.WriteLine("Saving screenshot " + name + " ..."); + GraphicsDevice.Wait(); - screenshotBuffer.GetData(screenshotPixels, screenShotBufferSize); - Texture.SavePNG("screenshot.png", 1280, 720, screenshotPixels); + screenshotBuffer.GetData(screenshotPixels, screenshotBufferSize); + + Texture.SavePNG( + name, + 1280, + 720, + GraphicsDevice.GetSwapchainFormat(Window), + screenshotPixels + ); + + System.Console.WriteLine("Screenshot saved!"); + + screenshotInProgress = false; + } + + protected override void OnDestroy() + { + } } } diff --git a/src/hexagon_grid.frag b/src/hexagon_grid.frag new file mode 100644 index 0000000..0244fe2 --- /dev/null +++ b/src/hexagon_grid.frag @@ -0,0 +1,550 @@ +// Created by inigo quilez - iq/2020 +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + + +// Everybody has to implement an hexagonal grid. This it mine. +// It does raycasting on it, efficiently (just a few muls per step) and robustly +// (works in integers). Each cell is visited only once and in the right order. +// Based on https://www.shadertoy.com/view/WtSBWK Check castRay() in line 92. + +// That, plus the fact the ambient occlusion is analytical means this shader should +// run smoothly even on a crappy phone. It does on mine! + +#version 450 + +layout(set = 1, binding = 0) uniform sampler2D iChannel0; +layout(set = 1, binding = 1) uniform sampler2D iChannel1; + +layout(set = 3, binding = 0) uniform UniformBlock +{ + float time; + vec2 resolution; +} Uniforms; + +//layout(location = 0) in vec2 fragCoord; + +layout(location = 0) out vec4 fragColor; + +// make this bigger if you have a storng PC +#define AA 2 + +// ----------------------------------------- +// mod3 - not as trivial as you first though +// ----------------------------------------- +int mod3( int n ) +{ + return (n<0) ? 2-((2-n)%3) : n%3; + + // Some methods of computing mod3: // PC-WebGL Native-OpenGL Android WebGL + // // -------- ------------- ------- + // 1. return (n<0) ? 2-((2-n)%3) : n%3; // Ok Ok Ok + // 2. return int((uint(n)+0x80000001U)%3u); // Ok Ok Broken + // 3. n %= 3; return (n<0)?n+3:n; // Ok Broken Ok + // 4. n %= 3; n+=((n>>31)&3); return n; // Ok Broken Ok + // 5. return ((n%3)+3)%3; // Ok Broken Ok + // 6. return int[](1,2,0,1,2)[n%3+2]; // Ok Broken Ok +} + +// -------------------------------------- +// hash by Hugo Elias) +// -------------------------------------- +int hash( int n ) { n=(n<<13)^n; return n*(n*n*15731+789221)+1376312589; } + + +// -------------------------------------- +// basic hexagon grid functions +// -------------------------------------- +ivec2 hexagonID( vec2 p ) +{ + const float k3 = 1.732050807; + vec2 q = vec2( p.x, p.y*k3*0.5 + p.x*0.5 ); + + ivec2 pi = ivec2(floor(q)); + vec2 pf = fract(q); + + int v = mod3(pi.x+pi.y); + + int ca = (v<1)?0:1; + int cb = (v<2)?0:1; + ivec2 ma = (pf.x>pf.y)?ivec2(0,1):ivec2(1,0); + + ivec2 id = pi + ca - cb*ma; + + return ivec2( id.x, id.y - (id.x+id.y)/3 ); +} + +vec2 hexagonCenFromID( in ivec2 id ) +{ + const float k3 = 1.732050807; + return vec2(float(id.x),float(id.y)*k3); +} + +// --------------------------------------------------------------------- +// the height function. yes, i know reading from a video texture is cool +// --------------------------------------------------------------------- +const float kMaxH = 6.0; + +float map( vec2 p, in float time ) +{ + p *= 0.5; + float f = 0.5+0.5*sin(0.53*p.x+0.5*time+1.0*sin(p.y*0.24))* + sin(0.13*p.y+time); + f*= 0.75+0.25*sin(1.7*p.x+1.32*time)*sin(1.3*p.y+time*2.1); + return kMaxH*(0.005+0.995*f); +} + +// -------------------------------------------------- +// raycast. this function is the point of this shader +// -------------------------------------------------- +vec4 castRay( in vec3 ro, in vec3 rd, in float time, + out ivec2 outPrismID, out int outFaceID ) +{ + ivec2 hid = hexagonID(ro.xz); + + vec4 res = vec4( -1.0, 0.0, 0.0, 0.0 ); + + const float k3 = 0.866025; + const vec2 n1 = vec2( 1.0,0.0); + const vec2 n2 = vec2( 0.5,k3); + const vec2 n3 = vec2(-0.5,k3); + + float d1 = 1.0/dot(rd.xz,n1); + float d2 = 1.0/dot(rd.xz,n2); + float d3 = 1.0/dot(rd.xz,n3); + float d4 = 1.0/rd.y; + + float s1 = (d1<0.0)?-1.0:1.0; + float s2 = (d2<0.0)?-1.0:1.0; + float s3 = (d3<0.0)?-1.0:1.0; + float s4 = (d4<0.0)?-1.0:1.0; + + ivec2 i1 = ivec2( 2,0); if(d1<0.0) i1=-i1; + ivec2 i2 = ivec2( 1,1); if(d2<0.0) i2=-i2; + ivec2 i3 = ivec2(-1,1); if(d3<0.0) i3=-i3; + + // traverse hexagon grid (in 2D) + bool found = false; + vec2 t1, t2, t3, t4; + for( int i=0; i<100; i++ ) + { + // fetch height for this hexagon + vec2 ce = hexagonCenFromID( hid ); + float he = 0.5*map(ce, time); + + // compute ray-hexaprism intersection + vec3 oc = ro - vec3(ce.x,he,ce.y); + t1 = (vec2(-s1,s1)-dot(oc.xz,n1))*d1; + t2 = (vec2(-s2,s2)-dot(oc.xz,n2))*d2; + t3 = (vec2(-s3,s3)-dot(oc.xz,n3))*d3; + t4 = (vec2(-s4,s4)*he-oc.y)*d4; + float tN = max(max(t1.x,t2.x),max(t3.x,t4.x)); + float tF = min(min(t1.y,t2.y),min(t3.y,t4.y)); + if( tN0.0 ) + { + found = true; + break; + } + + // move to next hexagon + if( t1.yres.x ) {res=vec4(t2.x,s2*vec3(n2.x,0,n2.y)); outFaceID=(d2<0.0)?-2: 2;} + if( t3.x>res.x ) {res=vec4(t3.x,s3*vec3(n3.x,0,n3.y)); outFaceID=(d3<0.0)?-3: 3;} + if( t4.x>res.x ) {res=vec4(t4.x,s4*vec3( 0.0,1,0)); outFaceID=(d4<0.0)? 4:-4;} + + outPrismID = hid; + } + + return res; +} + +// ------------------------------------------------------------------------- +// same as above, but simpler sinec we don't need the normal and primtive id +// -------------------------------------------------------------------------- +float castShadowRay( in vec3 ro, in vec3 rd, in float time ) +{ + float res = 1.0; + + ivec2 hid = hexagonID(ro.xz); + + const float k3 = 0.866025; + const vec2 n1 = vec2( 1.0,0.0); + const vec2 n2 = vec2( 0.5,k3); + const vec2 n3 = vec2(-0.5,k3); + + float d1 = 1.0/dot(rd.xz,n1); + float d2 = 1.0/dot(rd.xz,n2); + float d3 = 1.0/dot(rd.xz,n3); + float d4 = 1.0/rd.y; + + float s1 = (d1<0.0)?-1.0:1.0; + float s2 = (d2<0.0)?-1.0:1.0; + float s3 = (d3<0.0)?-1.0:1.0; + float s4 = (d4<0.0)?-1.0:1.0; + + ivec2 i1 = ivec2( 2,0); if(d1<0.0) i1=-i1; + ivec2 i2 = ivec2( 1,1); if(d2<0.0) i2=-i2; + ivec2 i3 = ivec2(-1,1); if(d3<0.0) i3=-i3; + + vec2 c1 = (vec2(-s1,s1)-dot(ro.xz,n1))*d1; + vec2 c2 = (vec2(-s2,s2)-dot(ro.xz,n2))*d2; + vec2 c3 = (vec2(-s3,s3)-dot(ro.xz,n3))*d3; + + // traverse regular grid (2D) + for( int i=0; i<8; i++ ) + { + vec2 ce = hexagonCenFromID( hid ); + float he = 0.5*map(ce, time); + + vec2 t1 = c1 + dot(ce,n1)*d1; + vec2 t2 = c2 + dot(ce,n2)*d2; + vec2 t3 = c3 + dot(ce,n3)*d3; + vec2 t4 = (vec2(1.0-s4,1.0+s4)*he-ro.y)*d4; + + float tN = max(max(t1.x,t2.x),max(t3.x,t4.x)); + float tF = min(min(t1.y,t2.y),min(t3.y,t4.y)); + if( tN < tF && tF > 0.0) + { + res = 0.0; + break; + } + + if( t1.y0.0 ) + { + // data at intersection point + vec3 pos = ro + rd*t; + vec3 nor = -tnor.yzw; + vec2 ce = hexagonCenFromID(prismID); + float he = map(ce,time); + int id = prismID.x*131 + prismID.y*57; + + // uvs + vec2 uv = (faceID==4) ? (pos.xz-ce)*0.15 : + vec2(atan(pos.x-ce.x,pos.z-ce.y)/3.14156, + (pos.y-he)/4.0 ); + uv += ce; + + // material color + vec3 mate = vec3(1.0); + id = hash(id); mate *= 0.1+0.9*float((id>>13)&3)/3.0; + id = hash(id); mate = ( ((id>>8)&15)==0 ) ? vec3(0.7,0.0,0.0) : mate; + vec3 tex = vec3(0.15,0.09,0.07)+0.75*pow(texture(iChannel0,uv.yx).xyz,vec3(1.0,0.95,0.9)); + mate *= tex; + + // lighting + float occ = calcOcclusion( pos, nor, time, prismID, faceID ); + + // diffuse + col = mate*pow(vec3(occ),vec3(0.95,1.05,1.1)); + + // specular + float ks = tex.x*2.0; + vec3 ref = reflect(rd,nor); + col *= 0.85; + float fre = clamp(1.0+dot(nor,rd),0.0,1.0); + col += vec3(1.1)*ks* + smoothstep(0.0,0.15,ref.y)* + (0.04 + 0.96*pow(fre,5.0))* + castShadowRay( pos+nor*0.001, ref, time ); + + // fog + col = mix(col,vec3(1.0), 1.0-exp2(-0.00005*t*t) ); + } + + return col; +} + +//----------------------------------------------- +// main = animate + render + color grade +//----------------------------------------------- +void main() +{ + vec2 fragCoord = gl_FragCoord.xy; + + fragCoord.y = Uniforms.resolution.y - gl_FragCoord.y; + + // init random seed + ivec2 q = ivec2(fragCoord); + + // sample pixel and time + vec3 tot = vec3(0.0); + for( int m=0; m1 + float d = 0.5+0.5*sin(fragCoord.x*147.0)*sin(fragCoord.y*131.0); + float time = Uniforms.time - 0.5*(1.0/24.0)*(float(m*AA+n)+d)/float(AA*AA); + #else + float time = Uniforms.time; + #endif + + // camera + float cr = -0.1; + float an = 3.0*time; + vec3 ro = vec3(0.1,13.0,1.0-an); + vec3 ta = vec3(0.0,12.0,0.0-an); + + // build camera matrix + vec3 ww = normalize( ta - ro); + vec3 uu = normalize(cross( ww,vec3(sin(cr),cos(cr),0.0) )); + vec3 vv = normalize(cross(uu,ww)); + // distort + p *= 0.9+0.1*(p.x*p.x*0.4 + p.y*p.y); + // buid ray + vec3 rd = normalize( p.x*uu + p.y*vv + 2.0*ww ); + + // dof + #if AA>1 + vec3 fp = ro + rd*17.0; + vec2 ra = texelFetch(iChannel1,(q+ivec2(13*m,31*n))&1023,0).xy; + ro.xy += 0.3*sqrt(ra.x)*vec2(cos(6.2831*ra.y),sin(6.2831*ra.y)); + rd = normalize( fp - ro ); + #endif + + // render + vec3 col = render( ro, rd, time ); + + // accumulate for AA + tot += col; + } + tot /= float(AA*AA); + + + // hdr->ldr tonemap + tot = tot*1.6/(1.0+tot); + tot = tot*tot*(3.0-2.0*tot); + + // gamma + tot = pow( clamp(tot,0.0,1.0), vec3(0.45) ); + + // color grade + vec2 p = fragCoord/Uniforms.resolution.xy; + tot.xyz += (p.xyy-0.5)*0.1; + + // vignetting + tot *= 0.5 + 0.5*pow( 16.0*p.x*p.y*(1.0-p.x)*(1.0-p.y), 0.1 ); + + // output + fragColor = vec4( tot, 1.0 ); +}