Samples

Following up on what rakoonic said in another thread, I suppose we could start putting examples up.

To start off, here’s one that does simplex noise (using code from Ashima Arts), with some time-based stuff thrown in.

-- generator.custom.simplex2 local kernel = {} kernel.language = "glsl" kernel.category = "generator" kernel.name = "simplex2" kernel.isTimeDependent = true kernel.fragment = [[// Uses 2D simplex noise from here: https://github.com/ashima/webgl-noise vec3 mod289(vec3 x) { return x - floor(x \* (1.0 / 289.0)) \* 289.0; } vec2 mod289(vec2 x) { return x - floor(x \* (1.0 / 289.0)) \* 289.0; } vec3 permute(vec3 x) { return mod289(((x\*34.0)+1.0)\*x); } float snoise(vec2 v) { const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 0.366025403784439, // 0.5\*(sqrt(3.0)-1.0) -0.577350269189626, // -1.0 + 2.0 \* C.x 0.024390243902439); // 1.0 / 41.0 // First corner vec2 i = floor(v + dot(v, C.yy) ); vec2 x0 = v - i + dot(i, C.xx); // Other corners vec2 i1; //i1.x = step( x0.y, x0.x ); // x0.x \> x0.y ? 1.0 : 0.0 //i1.y = 1.0 - i1.x; i1 = (x0.x \> x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); // x0 = x0 - 0.0 + 0.0 \* C.xx ; // x1 = x0 - i1 + 1.0 \* C.xx ; // x2 = x0 - 1.0 + 2.0 \* C.xx ; vec4 x12 = x0.xyxy + C.xxzz; x12.xy -= i1; // Permutations i = mod289(i); // Avoid truncation effects in permutation vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 )); vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); m = m\*m ; m = m\*m ; // Gradients: 41 points uniformly over a line, mapped onto a diamond. // The ring size 17\*17 = 289 is close to a multiple of 41 (41\*7 = 287) vec3 x = 2.0 \* fract(p \* C.www) - 1.0; vec3 h = abs(x) - 0.5; vec3 ox = floor(x + 0.5); vec3 a0 = x - ox; // Normalise gradients implicitly by scaling m // Approximation of: m \*= inversesqrt( a0\*a0 + h\*h ); m \*= 1.79284291400159 - 0.85373472095314 \* ( a0\*a0 + h\*h ); // Compute final noise value at P vec3 g; g.x = a0.x \* x0.x + h.x \* x0.y; g.yz = a0.yz \* x12.xz + h.yz \* x12.yw; return 130.0 \* dot(m, g); } P\_COLOR vec4 FragmentKernel( P\_UV vec2 texCoord ) { texCoord.xy \*= 25.0; texCoord.x += CoronaTotalTime \* 2.7; texCoord.y \*= sin(CoronaTotalTime \* 1.3); P\_COLOR float sample = snoise(texCoord); return CoronaColorScale( vec4(sample, sample, sample, 1) ); }]] return kernel

and the driver:

-- -- Abstract: CustomEffect sample app -- -- Sample code is MIT licensed, see http://www.coronalabs.com/links/code/license -- Copyright (C) 2015 Corona Labs Inc. All Rights Reserved. -- -- Supports Graphics 2.0 -- -- v1.0 2/4/2015 ------------------------------------------------------------ -- Setup local easing = require "easing" display.setStatusBar( display.HiddenStatusBar ) local image = display.newCircle(0, 0, 250)--Image( "Image1.jpg" ) image:scale( 0.5, 0.5 ) image.x = display.contentCenterX image.y = display.contentCenterY ------------------------------------------------------------ -- Debugging: -- Log compiler errors found in shader code to console display.setDefault( 'isShaderCompilerVerbose', true ) -- Load custom filter local kernel = require "kernel\_generator\_custom\_simplex2" --"kernel\_filter\_custom\_example" graphics.defineEffect( kernel ) -- Apply filter image.fill.effect = "generator.custom.simplex2" --[["filter.custom.example" -- image:setFillColor( 1, 0, 0 ) -- Filter parameter image.fill.effect.intensity = 1 transition.to( image.fill.effect, { intensity = 0, time = 5000, transition = easing.outExpo } ) --]] ------------------------------------------------------------

This is super cool! It’s fun playing with the frequency (the 1.3) and dialing it up and down.

This would be awesome to put on the code exchange (there’s a “shader” category you can select):

http://code.coronalabs.com/

Will do. I wasn’t sure how secret all this beta testing is supposed to be.  :slight_smile:

I’ll expose some of those parameters when I do.

Over time I’ll probably start putting mixins like noise () into their own module so the fragment could just be written as, say:

kernel.fragment = shader\_helpers.SimplexNoise2 .. [[ P\_COLOR vec4 FragmentKernel( P\_UV vec2 texCoord ) { -- etc...

as a poor man’s #include. Although this will make the line count a bit confusing.  :slight_smile:

It’s up in the Code Exchange.

I saw this asked on the forum, and decided to give it a go.

So far I’ve got:

local kernel = {} kernel.language = "glsl" kernel.category = "filter" -- By default, the group is "custom" -- kernel.group = "custom" kernel.name = "swelter" kernel.isTimeDependent = true -- Expose effect parameters using vertex data kernel.vertexData = { { name = "extend", -- The property name exposed to Lua default = 5, min = 1, max = 250, index = 0, -- This corresponds to CoronaVertexUserData.x }, { name = "frequency", -- The property name exposed to Lua default = 1.3, min = .2, max = 8, index = 1, -- This corresponds to CoronaVertexUserData.y }, { name = "center", -- The property name exposed to Lua default = display.contentCenterX, min = 0, max = display.contentWidth, index = 2, -- This corresponds to CoronaVertexUserData.2 } } kernel.vertex = [[varying P\_UV float extension; // Custom varying variable P\_POSITION vec2 VertexKernel( P\_POSITION vec2 position ) { P\_DEFAULT float ntexels = CoronaVertexUserData.x \* CoronaContentScale.x; extension = sign(position.x - CoronaVertexUserData.z) \* ntexels; position.x += extension; return position; }]] kernel.fragment = [[varying P\_UV float extension; // Custom varying variable P\_COLOR vec4 FragmentKernel( P\_UV vec2 texCoord ) { P\_UV float amp = sin(2. \* 3.14159 \* (texCoord.y \* CoronaVertexUserData.y + CoronaTotalTime)); texCoord.x += amp \* extension \* CoronaTexelSize.z; if (abs(2. \* texCoord.x - 1.) \> 1.) discard; return CoronaColorScale(texture2D( CoronaSampler0, texCoord )); }]] return kernel

and

--- Driver for shader demo. -- -- Permission is hereby granted, free of charge, to any person obtaining -- a copy of this software and associated documentation files (the -- "Software"), to deal in the Software without restriction, including -- without limitation the rights to use, copy, modify, merge, publish, -- distribute, sublicense, and/or sell copies of the Software, and to -- permit persons to whom the Software is furnished to do so, subject to -- the following conditions: -- -- The above copyright notice and this permission notice shall be -- included in all copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- -- [MIT license: http://www.opensource.org/licenses/mit-license.php] -- -- Setup local widget = require( "widget" ) display.setStatusBar( display.HiddenStatusBar ) local image = display.newImageRect("Image1.jpg", 256, 256) image.x = display.contentCenterX image.y = display.contentCenterY ------------------------------------------------------------ -- Debugging: -- Log compiler errors found in shader code to console display.setDefault( 'isShaderCompilerVerbose', true ) -- Load custom filter local kernel = require "kernel\_filter\_custom\_swelter" graphics.defineEffect( kernel ) -- Apply filter image.fill.effect = "filter.custom.swelter" ------------------------------------------------------------ local function SliderAndText (top, prop, min\_value, max\_value) local range = max\_value - min\_value -- see the shader file for these values local slider = widget.newSlider{ top = top, left = 10, orientation = "horizontal", value = 100 \* (image.fill.effect[prop] - min\_value) / range, listener = function(event) image.fill.effect[prop] = min\_value + (event.value / 100) \* range end } local text = display.newText(prop, 0, slider.y, native.systemFont, 20) text.anchorX, text.x = 0, slider.contentBounds.xMax + 10 end -- Create scale, speed, frequency sliders SliderAndText(10, "extend", 1, 250) SliderAndText(40, "frequency", .2, 8)

I don’t like the “center” thing, but I don’t know if there’s a cleaner way around it. Any hints appreciated, if I’ve missed something obvious. :) For other vertex shaders I suspect (but don’t yet know) that I might need the angle and / or coordinate axes for rotation invariance, instead. In the fragment shader the texture coordinates usually give us that, of course.

Also, the line numbers for errors seem to get thrown off when both a vertex and fragment shader are present.

I guess these code snippets are getting big fast, so I’ll go with Gists or Bitbucket snippets or Code Exchange from here on out.

The simplex noise was posted here in the Code Exchange, and I’ve just added a demo of the now-working heat shader here.

I have a few more “low-hanging fruit” shaders to do (some 3D shape generators, ice, maybe a plasma) and then I think I’ll get a bit quieter as I attempt some more involved things.  :slight_smile: If it would be useful to anybody else, I’ll start a “tricks of the trade” topic for non-obvious techniques that I’ve found to come in handy, and others could chime in with their own. (Also, if anybody wants interesting ideas to explore, I’ve got plenty to spare…)

Okay, looks like I’ll just go with a Dropbox link to the current version instead, since I dragged in a whole bunch of Git submodules and it would be a chore to share otherwise.

I’ve added some sphere stuff, ice, bump mapping, outlines, and some “rip” effects (sort of like unzipping a texture down a seam), plus moved a whole bunch of stuff into a mixins module. I have packing for two [0,1] numbers to a single mediump , and some perfunctory UI helpers for the same. There’s some library-side support as well, but it all assumes the aforementioned packing scheme (and also single-pass shaders), so it may involve some work to introduce others.  :( Related to all of that is some work on the “setters” idea I mentioned in the other thread. So far everything still goes through vertex data, even for 5 or more parameters.

This all works simulator-side. Unfortunately, it falls apart on my Xoom (Android 4.1.2).  :(  :(  :( I did switch any use I made of  highp precision to mediump (i.e., P_DEFAULT to P_POSITION ), but no dice; on the plus side, nothing broke on the simulator, so I’m retaining the changes. I’ll install the Android Tools on another machine, later tonight if possible, and see if I can track anything down. That said, I’d definitely appreciate knowing if the demos work or fail elsewhere!

One thing I did notice with these, on the Xoom, is that there seems to be a lag, where I get a white rect (or nothing?) until I switch tabs. Then, in lieu of the shader, I only see the image, in the cased where I’m shading an image object. That same lag also seems to show up when I use the sliders, where sometimes I’ll get an error suggesting that the target.fill.effect doesn’t yet exist (specifically, I’ve noticed this in the “Rip” demo).

Anyhow, I’ll probably just update what’s in the linked zip every now and then. I might add some more 3D shapes, but probably first fill in the “Break” and “Curve” demos. The former will entail fleshing out some texture coordinate helpers and the latter will probably be my first go at using uniforms (to achieve adjacency, say via one “node” and its two neighbors, each being a  vec4 or mat4 uniform).

EDIT : Prepending a

#ifdef GL\_ES precision mediump float; #endif

has resolved most of the issues, and the rip thing was some silliness of my own.

Hi!
 
I got the folowing error trying to run main.lua of your heat haze shader
 
[lua]main.lua:33: ERROR: display.setDefault() given invalid key (isShaderCompilerVerbose)[/lua]

Do I need an Enterprise account to try it?

Are you using daily build 2015.2560 or later?

Uh, my fault. Updating to the latest build. Thank you Walter!

This is super cool! It’s fun playing with the frequency (the 1.3) and dialing it up and down.

This would be awesome to put on the code exchange (there’s a “shader” category you can select):

http://code.coronalabs.com/

Will do. I wasn’t sure how secret all this beta testing is supposed to be.  :slight_smile:

I’ll expose some of those parameters when I do.

Over time I’ll probably start putting mixins like noise () into their own module so the fragment could just be written as, say:

kernel.fragment = shader\_helpers.SimplexNoise2 .. [[ P\_COLOR vec4 FragmentKernel( P\_UV vec2 texCoord ) { -- etc...

as a poor man’s #include. Although this will make the line count a bit confusing.  :slight_smile:

It’s up in the Code Exchange.

I saw this asked on the forum, and decided to give it a go.

So far I’ve got:

local kernel = {} kernel.language = "glsl" kernel.category = "filter" -- By default, the group is "custom" -- kernel.group = "custom" kernel.name = "swelter" kernel.isTimeDependent = true -- Expose effect parameters using vertex data kernel.vertexData = { { name = "extend", -- The property name exposed to Lua default = 5, min = 1, max = 250, index = 0, -- This corresponds to CoronaVertexUserData.x }, { name = "frequency", -- The property name exposed to Lua default = 1.3, min = .2, max = 8, index = 1, -- This corresponds to CoronaVertexUserData.y }, { name = "center", -- The property name exposed to Lua default = display.contentCenterX, min = 0, max = display.contentWidth, index = 2, -- This corresponds to CoronaVertexUserData.2 } } kernel.vertex = [[varying P\_UV float extension; // Custom varying variable P\_POSITION vec2 VertexKernel( P\_POSITION vec2 position ) { P\_DEFAULT float ntexels = CoronaVertexUserData.x \* CoronaContentScale.x; extension = sign(position.x - CoronaVertexUserData.z) \* ntexels; position.x += extension; return position; }]] kernel.fragment = [[varying P\_UV float extension; // Custom varying variable P\_COLOR vec4 FragmentKernel( P\_UV vec2 texCoord ) { P\_UV float amp = sin(2. \* 3.14159 \* (texCoord.y \* CoronaVertexUserData.y + CoronaTotalTime)); texCoord.x += amp \* extension \* CoronaTexelSize.z; if (abs(2. \* texCoord.x - 1.) \> 1.) discard; return CoronaColorScale(texture2D( CoronaSampler0, texCoord )); }]] return kernel

and

--- Driver for shader demo. -- -- Permission is hereby granted, free of charge, to any person obtaining -- a copy of this software and associated documentation files (the -- "Software"), to deal in the Software without restriction, including -- without limitation the rights to use, copy, modify, merge, publish, -- distribute, sublicense, and/or sell copies of the Software, and to -- permit persons to whom the Software is furnished to do so, subject to -- the following conditions: -- -- The above copyright notice and this permission notice shall be -- included in all copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- -- [MIT license: http://www.opensource.org/licenses/mit-license.php] -- -- Setup local widget = require( "widget" ) display.setStatusBar( display.HiddenStatusBar ) local image = display.newImageRect("Image1.jpg", 256, 256) image.x = display.contentCenterX image.y = display.contentCenterY ------------------------------------------------------------ -- Debugging: -- Log compiler errors found in shader code to console display.setDefault( 'isShaderCompilerVerbose', true ) -- Load custom filter local kernel = require "kernel\_filter\_custom\_swelter" graphics.defineEffect( kernel ) -- Apply filter image.fill.effect = "filter.custom.swelter" ------------------------------------------------------------ local function SliderAndText (top, prop, min\_value, max\_value) local range = max\_value - min\_value -- see the shader file for these values local slider = widget.newSlider{ top = top, left = 10, orientation = "horizontal", value = 100 \* (image.fill.effect[prop] - min\_value) / range, listener = function(event) image.fill.effect[prop] = min\_value + (event.value / 100) \* range end } local text = display.newText(prop, 0, slider.y, native.systemFont, 20) text.anchorX, text.x = 0, slider.contentBounds.xMax + 10 end -- Create scale, speed, frequency sliders SliderAndText(10, "extend", 1, 250) SliderAndText(40, "frequency", .2, 8)

I don’t like the “center” thing, but I don’t know if there’s a cleaner way around it. Any hints appreciated, if I’ve missed something obvious. :) For other vertex shaders I suspect (but don’t yet know) that I might need the angle and / or coordinate axes for rotation invariance, instead. In the fragment shader the texture coordinates usually give us that, of course.

Also, the line numbers for errors seem to get thrown off when both a vertex and fragment shader are present.

I guess these code snippets are getting big fast, so I’ll go with Gists or Bitbucket snippets or Code Exchange from here on out.

The simplex noise was posted here in the Code Exchange, and I’ve just added a demo of the now-working heat shader here.

I have a few more “low-hanging fruit” shaders to do (some 3D shape generators, ice, maybe a plasma) and then I think I’ll get a bit quieter as I attempt some more involved things.  :slight_smile: If it would be useful to anybody else, I’ll start a “tricks of the trade” topic for non-obvious techniques that I’ve found to come in handy, and others could chime in with their own. (Also, if anybody wants interesting ideas to explore, I’ve got plenty to spare…)

Okay, looks like I’ll just go with a Dropbox link to the current version instead, since I dragged in a whole bunch of Git submodules and it would be a chore to share otherwise.

I’ve added some sphere stuff, ice, bump mapping, outlines, and some “rip” effects (sort of like unzipping a texture down a seam), plus moved a whole bunch of stuff into a mixins module. I have packing for two [0,1] numbers to a single mediump , and some perfunctory UI helpers for the same. There’s some library-side support as well, but it all assumes the aforementioned packing scheme (and also single-pass shaders), so it may involve some work to introduce others.  :( Related to all of that is some work on the “setters” idea I mentioned in the other thread. So far everything still goes through vertex data, even for 5 or more parameters.

This all works simulator-side. Unfortunately, it falls apart on my Xoom (Android 4.1.2).  :(  :(  :( I did switch any use I made of  highp precision to mediump (i.e., P_DEFAULT to P_POSITION ), but no dice; on the plus side, nothing broke on the simulator, so I’m retaining the changes. I’ll install the Android Tools on another machine, later tonight if possible, and see if I can track anything down. That said, I’d definitely appreciate knowing if the demos work or fail elsewhere!

One thing I did notice with these, on the Xoom, is that there seems to be a lag, where I get a white rect (or nothing?) until I switch tabs. Then, in lieu of the shader, I only see the image, in the cased where I’m shading an image object. That same lag also seems to show up when I use the sliders, where sometimes I’ll get an error suggesting that the target.fill.effect doesn’t yet exist (specifically, I’ve noticed this in the “Rip” demo).

Anyhow, I’ll probably just update what’s in the linked zip every now and then. I might add some more 3D shapes, but probably first fill in the “Break” and “Curve” demos. The former will entail fleshing out some texture coordinate helpers and the latter will probably be my first go at using uniforms (to achieve adjacency, say via one “node” and its two neighbors, each being a  vec4 or mat4 uniform).

EDIT : Prepending a

#ifdef GL\_ES precision mediump float; #endif

has resolved most of the issues, and the rip thing was some silliness of my own.

Hi!
 
I got the folowing error trying to run main.lua of your heat haze shader
 
[lua]main.lua:33: ERROR: display.setDefault() given invalid key (isShaderCompilerVerbose)[/lua]

Do I need an Enterprise account to try it?

Are you using daily build 2015.2560 or later?

Uh, my fault. Updating to the latest build. Thank you Walter!