About Monkey 2 › Forums › Monkey 2 Development › Playing with shaders
This topic contains 26 replies, has 8 voices, and was last updated by 
 peterigz
 2 years, 2 months ago.
- 
		AuthorPosts
 - 
		
			
				
January 10, 2017 at 11:34 pm #6392
Gave myself a quick crash course in shaders and applied it a bit in monkey2 for fun. If you want the attached to run then you’ll have to make a few minor additions to mojo code so maybe not for the faint hearted but if you fancy having a play then this is what you do: (the modified files are attached as well, but just so you know what changed)
By default, the mojo vertexbuffer contains position, 2 texcoords and color, but I needed to pass a variable to the shader that I could use to create animated shaders. So to do that:
mojo.graphics.vertex.monkey2: add and extra field (time:float) to the vertex2 struct:
Monkey123456789Struct Vertex2fField position:Vec2fField texCoord0:Vec2fField texCoord1:Vec2fField color:UIntField time:FloatEndthen in mojo/graphics/canvas.monkey2 theres 4 different “AddVertex” methods, in each one add:
Monkey1_vp->time=Millisecs()So basically we’ll be passing the time which we can use to create a varying number to animate with.
Then in mojo/graphics/vertexbuffer.monkey2 you need to change the Pitch property to return 32 (the new size of the vertex2 struct) and add a new attrib pointer in the Bind() method:
Monkey1glEnableVertexAttribArray( 4 ) ; glVertexAttribPointer( 4,1,GL_FLOAT,False,Pitch,Cast<Void Ptr>( 28 ) )Then finally in mojo/graphics/shader.monkey2 you need to add the new attrib location:
Monkey1glBindAttribLocation( glprogram,4,"mx2_Time" )So then in your shader you can create your “attribute float mx2_Time;” which will get the time value that’s set in AddVertex. Look at noise.glsl I added more comments in there.
It’s a messy hack but fun to play with
If you want to see what effect this creates without downloading and trying for yourself I took the shaders “Quasicrystal” and “Noise” from here: http://pixelshaders.com/examples/
It’d be nice if there was an easier way of doing this, but I can see there’s lots of things that would need to be taken into consideration, maybe we can come up with something?
January 10, 2017 at 11:45 pm #6395Nice work, and very similiar to what I did to get a little 3d engine going, ie: modify the Vertex struct and rewrite some shaders!
Do you really need ‘time’ to be a vertex attribute here though? It’s likely to be the same for most (all if your fast enough) vertices, in which case it should really be a ‘uniform’.
This is where the ‘UniformBlock’ thing comes in – have a look in Cavas for how shader _uniforms are set, and at UniformBlock.Init() for how uniform names are bound. You could bind mx2_Time in UniformBlock.Init() and set mx2_Time somewhere in canvas.
January 10, 2017 at 11:55 pm #6396Thanks Mark, I looked at uniformblock but was struggling to make sense of how it all fits together so went the easier (to me) route with the vertex buffer just to see if I can make it work, still not sure what I’m doing!
Will take a closer look at it now I’m a bit clearer
January 11, 2017 at 4:09 am #6401This is really cool!
I still can’t wrap my head around GL shaders, though… the syntax is fine, but I can never understand how the stuff happening in the shader gets passed to the graphics card… like, are there pre-determined variables that do pre-determined things the driver expects that I’m supposed to use?January 11, 2017 at 4:09 am #6402Nice approach, this gives me lots of ideas.
From one hand I would like to see this type of functionality given out of the box in mojo. This way the majority of users would simply to download pre-made shaders made by the community and use them asap.
Imagine instead of having a blue shape for your water, to have a real crystal clear water. This would be super cool.
Another approach to this solution would be:
‘ usage example
canvas.UseShader(myCustomShader)
canvas.DrawRect …
canvas.StopUsingShader()
On canvas.monkey2 (line 1229) you would have:
Field _shader:Shader ‘ the original shader
Field _shaderCustom:Shader ‘ the custom shader
Field _useCustomShader:Bool ‘ the flag to control the custom shader usageIn Method AddDrawOp (line 1409) you would have:
If _useCustomShader _drawOp.shader=_shaderCustom Else _drawOp.shader=shaderAnother guarantee for safety is that there should be a validation mechanism that would ensure that the most essential shader attributes and uniforms (starting by the prefix mx2_) are there, this would prevent any errors.
P.S. Also in this example you posted peterigz, the “time” could become a uniform, check here my GLES code to see how the uniforms are used. The “matrix” that is used as the camera:
http://monkey2.monkey-x.com/forums/topic/monkey2-opengl-experiments/January 11, 2017 at 9:00 am #6407@ethernaut – don’t know if this will help but shader output is(edit isn’t!!) “passed” to the graphics card, its actually *running* on the GPU itself, there used to be a bunch of “built in” variables, but in GLES2.0 there is just vertex and fragment output gl_Position and gl_FragColor (in later versions you just define any old variable as “out”)
I found this book most useful http://www.opengles-book.com/es2/ (yipes that was more than 6 years ago!!!)
January 11, 2017 at 6:41 pm #6417In general terms all of the graphics theory is based on software renderers, OpenGL allows you to have this renderer but in the difference that it exists as hardware and you are given an API to access it and use it.
January 11, 2017 at 9:06 pm #6425I can never understand how the stuff happening in the shader gets passed to the graphics card…
Your shader programs are actually executed on the GPU.
GPU still renders triangles, but it calls your vertex shader to get the position of each triangle vertex. Vertex shader does this by writing to the built-in gl_Position var. The inputs to the vertex shader program are the vertex buffer data (ie: local position, texcoords, normal etc) and the vertex shader needs to somehow transform the position value to clip space (one model->view->projection matrix multiply is often enough) and write the other to varyings (see below) for interpolation.
GPU then draws a triangle, scanline by scanline just the way you would in software, only it calls your fragment shader to evaluate the color to actually render each pixel (aka fragment) to the draw buffer. Fragment shader does this by writing to the built-in gl_FragColor var. The inputs to the fragment shader are ‘varying’ values that you have previously set up in the vertex shader – things like vertex color, texcoords etc. The GPU interpolates these for you while it generates the triangle.
January 11, 2017 at 9:29 pm #6426From one hand I would like to see this type of functionality given out of the box in mojo.
Same here, but time=money etc. I’d already spent a month or 2 on 2d and couldn’t really justify spending more time on the graphics ‘subsystem’, esp. when I had no ‘alternate’ use case for it, such as mojo3d! Plus people were waiting on better IDE, reflection, mobile, website, docs etc etc.
January 11, 2017 at 11:50 pm #6428Let’s hope that things will go smooth.
January 13, 2017 at 2:04 am #6474I tried out uniform as Mark/Cocon suggested and it’s much easier. Had a little play and made it easier to add your own uniforms by adding a new command:
Monkey1AddUserUniform:UserUniform(names:String[], index:Int, callback:Void(useruniform:UserUniform, uniforms:UniformBlock) )Which lets you define your own uniform (this should match what you put in your shader), and lets you define a callback which will be called every time the canvas is rendered. For example the demo program now looks like:
Monkey12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152Namespace myapp#Import "<std>"#Import "<mojo>"#Import "assets/"Using std..Using mojo..Class MyWindow Extends WindowField image:ImageField shader:ShaderField timeuniform:UserUniformMethod New( title:String="Simple mojo app",width:Int=800,height:Int=600,flags:WindowFlags=Null )Super.New( title,width,height,flags )'shader = New Shader("Red", LoadString("asset::wave.glsl"))shader = New Shader("Red", LoadString("asset::noise.glsl"))timeuniform = AddUserUniform(New String[]("mx2_Time2"), 0, UniformCallback)image = Image.Load("asset::clouds.png", shader)EndMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()canvas.Clear(New Color(0,0,0,1))canvas.DrawImage(image, 100, 50)EndFunction UniformCallback(useruniform:UserUniform, uniforms:UniformBlock)uniforms.SetScalar( "mx2_Time2", Float(Millisecs())/1000.0)EndEndFunction Main()New AppInstanceNew MyWindowApp.Run()EndWhich is reasonably neat. You could extend “UserUniform” Class to put your own fields in there to help pass whichever specific variables you need to as the UserUniform instance is passed into the callback function as well. I’ve attached the modified files again, only Canvas and UniformBlock needed changing this time.
Attachments:
January 13, 2017 at 8:24 am #6483Looks great peterigz
Would you be able to tacle a shader that does antialiasing, as Mark suggests here : http://monkey2.monkey-x.com/forums/topic/poor-mans-antialias/#post-6472
It is too specialized for me at the moment, and would be a great addition to MX2.
January 14, 2017 at 12:02 am #6493I think that’s a bit beyond me! The smaa shader looks good, but it looks a bit advanced to implement, I’m pretty new to shaders.
Which brings me onto another question: If we wanted to apply a shader to the whole screen after it’s rendered everything else where would you look in mojo? I can see where it does the lighting and shadow rendor passes so I’m guessing after that somewhere.
January 14, 2017 at 10:03 am #6495One technique is to render the whole game to a texture and then use that texture to render a whole screen sized poly using your sfx shader, you need decent hardware
It should also noted that while graphics effects do add to presentation they do nothing for game play or balance or any of the other important aspects of a crackimg game, ive played some fantastic games that look quite plain – worth thinking about….
January 17, 2017 at 2:15 pm #6582 - 
		AuthorPosts
 
You must be logged in to reply to this topic.
