Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --# Notes
- --[[
- This project shows how to add different types of lighting to 3D scenes (in this case, a cube)
- --HOW TO USE THIS PROJECT
- There are a number of tabs at the top. Press on a tab to see its code.
- Work through the tabs from left to right, to see the project develop
- You can run the code in any of the project tabs, by
- 1. running the program, and
- 2. selecting the tab number using the controls at the upper left of the screen
- The program will remember your selection after that.
- This enables you to work with one tab at a time, make changes and see the effects by running the program
- Each tab has all the code for that demo, so you can copy the code in any tab to your own project and adapt it
- IMPORTANT NOTES
- 1. Lighting requires the use of shaders. While you can use this code as a black box, it is
- preferable that you understand how it works, so you can make changes or fix problems yourself.
- 2. It may be difficult to learn how lighting works just from looking at these demos. Ideally, you should
- try to learn the basics of diffuse and specular light first.
- --]]
- --# Ambient
- --Ambient light
- --[[
- Ambient light is the general lighting level
- You could call it the minimum light that falls on every surface
- if it is 0, then anything that is not lit will be pitch black
- if it is 1, then everything is fully lit all the time
- As you might guess, it is pretty simple - you just multiply the light level by the ambient factor
- There's just one thing - the ambient light doesn't have to be white, so we specify the light colour
- and everything will be tinted by this colour (see the extra parameter provided for this)
- You are unlikely ever to use just ambient light on its own.
- --]]
- function setup()
- block=CreateBlock(1,0.5,0.75,"Platformer Art:Block Brick")
- currentModelMatrix=modelMatrix() --this is used for touches, not lighting
- SetupShader()
- end
- --All the lighting code is kept in these two functions to make it easier for you to see it and use it yourself
- function SetupShader() --set up the lighting
- --in these demos, you get to change some of the lighting options using parameters
- --in your own apps, you will probably just set the lighting values yourself
- parameter.number("ambientStrength", 0, 1, 0.3) -- level of ambient light (0=dark, 1=light)
- parameter.color("ambientColor", color(255,255,255,255)) --- colour of ambient light
- block.shader=shader(AmbientShader.vertexShader,AmbientShader.fragmentShader)
- end
- function UpdateShader() --update the lighting details before drawing
- --if you aren't going to be changing ambient colour or strength, you can put this line in setup
- block.shader.ambient = ambientColor*ambientStrength
- end
- --this shader has ambient lighting
- AmbientShader = {
- vertexShader = [[
- uniform mat4 modelViewProjection;
- uniform vec4 ambient; // ambient light
- attribute vec4 position;
- attribute vec4 color;
- attribute vec2 texCoord;
- varying lowp vec4 vColor;
- varying highp vec2 vTexCoord;
- void main()
- {
- vColor = color*ambient; // apply ambient light to object colour and pass to fragment shader
- vTexCoord = texCoord;
- gl_Position = modelViewProjection * position;
- }
- ]],
- fragmentShader = [[
- precision highp float;
- uniform lowp sampler2D texture;
- varying lowp vec4 vColor;
- varying highp vec2 vTexCoord;
- void main()
- {
- lowp vec4 col = texture2D(texture, vTexCoord) * vColor; //apply ambient light to texture
- col.a = 1.0;
- gl_FragColor = col;
- }
- ]]
- }
- --UTILITY CODE - IT WILL NOT CHANGE FROM HERE ON --
- function draw()
- background(0)
- perspective()
- camera(-1,1,2,0,0,0)
- HandleTouches() --allows you to rotate the cube with your fingers
- UpdateShader()
- block:draw()
- end
- --this code manages touches so you can rotate the cube
- function HandleTouches()
- modelMatrix(currentModelMatrix) --apply the stored settings
- --do rotation for touch
- if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
- rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
- rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
- currentModelMatrix = modelMatrix() --store the resulting settings for next time
- end
- end
- --this function creates a cube
- function CreateBlock(w,h,d,tex,col,pos,ms) --width,height,depth,texture,colour,position,mesh (if existing already)
- pos=pos or vec3(0,0,0)
- local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2
- local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)}
- local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7],
- v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]}
- local texCoords
- if tex then
- local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)}
- texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],
- t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]}
- end
- local norm={} --calculate normals
- for i=1,#vert,3 do
- local n=GetNormal(vert[i],vert[i+1],vert[i+2])
- norm[i],norm[i+1],norm[i+2]=n,n,n
- end
- if not ms then ms=mesh() end
- if ms.size==0 then
- ms.vertices=vert
- ms.normals=norm
- ms.texture,ms.texCoords=tex,texCoords
- else
- for i=1,#vert do
- table.insert(ms.vertices,vert[i])
- table.insert(ms.normals,norm[i])
- if tex then table.insert(ms.texCoords,texCoords[i]) end
- end
- end
- ms:setColors(col or color(255))
- return ms
- end
- function GetNormal(v1,v2,v3)
- return ((v1-v3):cross(v2-v3)):normalize()
- end
- function PrintExplanation()
- output.clear()
- print("Ambient light is the general background light that shines equally on all sides of an object")
- print("Use a finger to rotate the cube")
- end
- --# Diffuse
- --Diffuse
- --[[
- The sun is so far away that its light comes from the same direction for every part of your scene, and the
- strength of the light is also the same across your scene. This is diffuse light - coming from a single direction
- with a constant colour strength.
- Diffuse light can have any colour, but because it is added to ambient light, the total of the two should not
- generally exceed 255. In other words, ambient + diffuse <= color(255)
- For example, you might use
- ambient = color(255) * 0.3 --dim white light
- diffuse = color(255,0,0) * 0.7 --red light
- When you add these together, the maximum light is color(255,76,76)
- Diffuse light is caused by reflection, and is brightest for any object directly facing the light, reducing if the
- light hits the object at an angle.
- Our objects are made from triangles of vertices. These triangles are flat surfaces, and the direction they are
- facing is at right angles to that surface. This direction is called a "normal" (based on a latin word meaning 'at right angles'). So if you imagine lying flat on your back on a triangle of vertices, you are looking in the
- 'normal' direction - and if that is facing the diffuse light source, the light will be brightest.
- You need two things to calculate the amount of diffuse light for any triangle
- 1. its 'normal' direction
- 2. a way of measuring how similar this is to the direction that the diffuse light is coming from
- This demo provides a function for calculating the normal of any set of three vertices, and Codea meshes have a
- normals property, so if you have a mesh m and a table of normals n, you can write
- m.normals = n
- (see the CreateBlock function below for an example)
- The dot function is a convenient way of measuring the similarity of two directions, and that is used in the
- shader to calculate the amount of reflection.
- Note that because the normals are the same for all the vertices in each triangle, they will be the same for each
- point in that triangle. So we can calculate diffuse light in the vertex shader, rather than the fragment shader,
- which is much more efficient.
- To set the direction the diffuse light is coming from, imagine a line running from (0,0,0) towards the diffuse
- light. Take any point on that line, and "normalise" it to give it a length of 1. So if I am facing toward -z as
- usual, here are some examples
- vec3(-1,0,0) --a light that shines from left to right (no need to normalize, it already has length 1)
- vec3(0,1,0) --a light that shines down vertically (again, no need to normalise)
- vec3(-1,0.5,1):normalize() --a light that comes from behind me over my left shoulder
- The absolute size of the numbers you use for x,y,z doesn't matter, since
- vec3(1,2,-3):normalize() = vec3(10,20,-30):normalize()
- but of course the relative size (how big x is, relative to y and z, for example) does matter..
- --]]
- function setup()
- block=CreateBlock(1,0.5,0.75,"Platformer Art:Block Brick")
- currentModelMatrix=modelMatrix() --this is used for touches, not lighting
- SetupShader()
- end
- --All the lighting code is kept in these two functions to make it easier for you to see it and use it yourself
- function SetupShader() --set up the lighting
- --in your own apps, you will probably just set these lighting values yourself instead of using parameters
- parameter.number("diffuseStrength", 0, 1, 0.7) --brightness of light source -- ***** NEW
- parameter.color("lightColor", color(255)) -- ***** NEW
- block.shader=shader(DiffuseShader.vertexShader,DiffuseShader.fragmentShader)
- block.shader.ambientColor = color(255)*0.3
- block.shader.directDirection = vec3(-1,0,1):normalize() -- ****** NEW
- end
- function UpdateShader() --update the lighting details before drawing
- --if you aren't going to be changing colours or light strength, you can put the next line in setup
- block.shader.directColor=diffuseStrength*lightColor -- ****** NEW
- block.shader.mModel=modelMatrix() --this must be updated each time you draw
- end
- --shader follows
- DiffuseShader = {
- vertexShader = [[
- uniform mat4 modelViewProjection;
- uniform vec4 ambientColor;
- uniform vec3 directDirection;
- uniform vec4 directColor;
- uniform mat4 mModel;
- attribute vec4 position;
- attribute vec4 color;
- attribute vec2 texCoord;
- attribute vec3 normal;
- varying lowp vec4 vColor;
- varying highp vec2 vTexCoord;
- void main()
- {
- vec4 norm = normalize(mModel * vec4( normal, 0.0 )); //transform normal to world speace
- float diffuse = max( 0.0, dot( norm.xyz, directDirection )); //calculate strength of reflection
- vColor = ambientColor + diffuse * directColor; //total color
- vColor.a = 1.0;
- vTexCoord = texCoord;
- gl_Position = modelViewProjection * position;
- }
- ]],
- fragmentShader = [[
- precision highp float;
- uniform lowp sampler2D texture;
- varying lowp vec4 vColor;
- varying highp vec2 vTexCoord;
- void main()
- {
- gl_FragColor = texture2D(texture, vTexCoord) * vColor;
- }
- ]]
- }
- --EVERYTHING BELOW IS UNCHANGED --
- function draw()
- background(0)
- perspective()
- camera(-1,1,2,0,0,0)
- HandleTouches()
- UpdateShader()
- block:draw()
- end
- function HandleTouches()
- modelMatrix(currentModelMatrix) --apply the stored settings
- --do rotation for touch
- if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
- rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
- rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
- currentModelMatrix = modelMatrix() --store the resulting settings for next time
- end
- end
- function CreateBlock(w,h,d,tex,col,pos,ms) --width,height,depth,texture,colour,position,mesh (if existing already)
- pos=pos or vec3(0,0,0)
- local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2
- local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)}
- local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7],
- v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]}
- local texCoords
- if tex then
- local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)}
- texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],
- t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]}
- end
- local norm={} --calculate normals
- for i=1,#vert,3 do
- local n=GetNormal(vert[i],vert[i+1],vert[i+2])
- norm[i],norm[i+1],norm[i+2]=n,n,n
- end
- if not ms then ms=mesh() end
- if ms.size==0 then
- ms.vertices=vert
- ms.normals=norm
- ms.texture,ms.texCoords=tex,texCoords
- else
- for i=1,#vert do
- table.insert(ms.vertices,vert[i])
- table.insert(ms.normals,norm[i])
- if tex then table.insert(ms.texCoords,texCoords[i]) end
- end
- end
- ms:setColors(col or color(255))
- return ms
- end
- function GetNormal(v1,v2,v3)
- return ((v1-v3):cross(v2-v3)):normalize()
- end
- function PrintExplanation()
- output.clear()
- print("Diffuse light comes from a specific direction, so far away that it is in the same direction (and has the same strength) for every part of the object you are lighting.\n\nExample - the sun")
- print("The part of the object facing the light direction will be lit the most")
- print("Use a finger to rotate the cube")
- end
- --# Specular
- --Specular
- --[[
- Specular light is the reflection from a light source into your eye (the bright spot!)
- It is too complex to explain fully here, but it needs the camera position
- The extra shader settings below are
- * specularPower = how focussed and concentrated the light is. Use a power of 2, eg 1,2,4,8,16,32,64,...
- (the demo has a Focus parameter which is from 1-10. It is converted to powers of 2, so 3 becomes 2^3 = 8)
- * shine - how shiny the object is (0 to 1)
- * eyePosition - (vec3(x,y,z) position of camera
- --]]
- function setup()
- block=CreateBlock(1,0.5,0.75,"Platformer Art:Block Brick")
- currentModelMatrix=modelMatrix() --this is used for touches, not lighting
- camPos=vec3(0,0,2) -- ***** NEW
- z,zd=0,-.01 --to move block back and forward
- SetupShader()
- end
- --All the lighting code is kept in these two functions to make it easier for you to see it and use it yourself
- function SetupShader() --set up the lighting
- --set position of light source
- block.shader=shader(SpecularShader.vertexShader,SpecularShader.fragmentShader)
- block.shader.ambientColor = color(255) *0.5
- block.shader.directColor=color(255,255,0) *0.7
- block.shader.directDirection = vec3(1,0,1):normalize()
- block.shader.shine=1 --very shiny -- ***** NEW
- --you can play with the size and focus of the spot using this parameter (make sure it is a power of 2)
- --convert the user choice to a power of 2
- parameter.integer("Focus",1,10,5,function() block.shader.specularPower=2^Focus end)
- end
- function UpdateShader() --update the lighting details before drawing
- block.shader.mModel=modelMatrix() --this must be updated each time you draw
- block.shader.eyePosition=camPos -- ***** NEW
- block.shader.mModel=modelMatrix()
- end
- function draw()
- background(0)
- perspective()
- currentModelMatrix[15]=z
- camera(camPos.x,camPos.y,camPos.z,0,0,0)
- HandleTouches()
- UpdateShader()
- block:draw()
- z=z+zd
- if z<-3 or z>0 then zd=-zd end
- end
- --shader follows
- SpecularShader={
- vertexShader=[[
- uniform mat4 modelViewProjection;
- uniform mat4 mModel;
- uniform vec4 directColor;
- uniform vec3 directDirection;
- uniform vec4 ambientColor;
- attribute vec4 position;
- attribute vec2 texCoord;
- attribute vec3 normal;
- varying lowp vec4 vColor;
- varying highp vec2 vTexCoord;
- varying lowp vec4 vPosition;
- varying lowp vec4 vNormal;
- varying vec4 vLight;
- void main()
- {
- vTexCoord = texCoord;
- vPosition = mModel * position;
- gl_Position = modelViewProjection * position;
- vNormal = mModel * vec4(normal, 0.0); //transform normal to world space
- vLight = ambientColor+directColor * max( 0.0, dot(normalize(vNormal.xyz), directDirection));
- }
- ]],
- fragmentShader=[[
- precision highp float;
- uniform lowp sampler2D texture;
- uniform vec4 directColor;
- uniform vec3 directDirection;
- uniform vec3 eyePosition;
- uniform float specularPower;
- uniform float shine;
- varying highp vec2 vTexCoord;
- varying lowp vec4 vPosition;
- varying lowp vec4 vNormal;
- varying vec4 vLight;
- vec4 norm = normalize(vNormal);
- vec4 eye = vec4(eyePosition, 1.0);
- vec4 direct = vec4(directDirection, 0.0);
- void main()
- {
- //calculate specular light
- vec4 cameraDirection = normalize( eye - vPosition );
- vec4 halfAngle = normalize( cameraDirection + direct );
- float spec = pow( max( 0.0, dot( norm, halfAngle)),specularPower);
- vec4 specLight = min(directColor + 0.5, 1.0) * spec * shine;
- vec4 totalColor=texture2D(texture, vTexCoord)*(vLight+specLight);
- totalColor.a= 1.;
- gl_FragColor=totalColor;
- }
- ]]
- }
- --EVERYTHING BELOW IS UNCHANGED --
- function HandleTouches()
- modelMatrix(currentModelMatrix) --apply the stored settings
- --do rotation for touch
- if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
- rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
- rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
- currentModelMatrix = modelMatrix() --store the resulting settings for next time
- end
- end
- function CreateBlock(w,h,d,tex,col,pos,ms) --width,height,depth,texture,colour,position,mesh (if existing already)
- pos=pos or vec3(0,0,0)
- local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2
- local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)}
- local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7],
- v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]}
- local texCoords
- if tex then
- local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)}
- texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],
- t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]}
- end
- local norm={} --calculate normals
- for i=1,#vert,3 do
- local n=GetNormal(vert[i],vert[i+1],vert[i+2])
- norm[i],norm[i+1],norm[i+2]=n,n,n
- end
- if not ms then ms=mesh() end
- if ms.size==0 then
- ms.vertices=vert
- ms.normals=norm
- ms.texture,ms.texCoords=tex,texCoords
- else
- for i=1,#vert do
- table.insert(ms.vertices,vert[i])
- table.insert(ms.normals,norm[i])
- if tex then table.insert(ms.texCoords,texCoords[i]) end
- end
- end
- ms:setColors(col or color(255))
- return ms
- end
- function GetNormal(v1,v2,v3)
- return ((v1-v3):cross(v2-v3)):normalize()
- end
- function PrintExplanation()
- output.clear()
- print("Specular light adds a bright spot to the reflection")
- print("The brightness depends on how much the light reflects off the surface toward the camera")
- print("Play with the focus setting to make the spot larger or smaller")
- print("Use a finger to rotate the cube")
- end
- --# Point
- --Point
- --[[
- Point light is light from a point in space (rather than from a direction, like diffuse light). The light shines in all directions, like a lightbulb.
- This means that the light may hit two points on a triangle at different angles, so we can't calculate it
- in the vertex shader. Additionally, point lights often have a limited range, so the brightness reduces
- with distance.
- To be clear about the differences with directional light, which we have worked with until now..
- Directional light (eg the sun)
- - light comes from the same direction for every part of the scene
- - the strength is constant
- Point light (eg light bulb)
- - the light is (usually) inside the scene, so it hits every part of the scene at different angles
- - the light shines in every direction from the position of the light
- - the strength of the light may vary with distance
- The shader in this demo allows you to have both types of light, and the shader variables have a "direct" or "point" prefix to make it clear which type they refer to.
- You can add more point lights if you want, just
- add extra shader variables (you'll note the point light variables all end in 1, so just duplicate all the variables
- with 2 on the end, for a second light (and adjust the shader to do the extra calculations)
- NOTE that directional light (as shown in the previous three demos) is included in this demo, but its light has
- been set to nil so that you can see the point light clearly
- --]]
- function setup()
- block=CreateBlock(1,0.5,0.75,"Platformer Art:Block Brick")
- currentModelMatrix=modelMatrix() --this is used for touches, not lighting
- camPos=vec3(0,0,2)
- z,zd=0,-.01
- SetupShader()
- end
- --All the lighting code is kept in these two functions to make it easier for you to see it and use it yourself
- function SetupShader() --set up the lighting
- --set position of light source
- block.shader=shader(PointShader.vertexShader,PointShader.fragmentShader)
- block.shader.ambientColor = color(255) *0.2
- block.shader.directColor=color(255) * 0 --NOTE-turned off so you can see the point light
- block.shader.directDirection = vec3(-1,0,1):normalize()
- block.shader.shine=0 --set this to 0 if you don't want specular lighting for your directional light
- block.shader.specularPower=32
- --point light variables needed by the shader --- ****** NEW
- block.shader.point1Position=vec3(2,0,1) --this is an actual position to the right of us
- block.shader.point1Color=color(255) --multiply by a factor (eg 0.5) if you want to reduce brightness
- block.shader.point1Range=6 --light reduces to nil over this distance
- end
- function UpdateShader() --update the lighting details before drawing
- block.shader.mModel=modelMatrix()
- block.shader.eyePosition=camPos
- end
- function draw()
- background(0)
- perspective()
- camera(camPos.x,camPos.y,camPos.z,0,0,0)
- currentModelMatrix[15]=z
- HandleTouches()
- UpdateShader()
- block:draw()
- z=z+zd
- if z<-6 or z>0 then zd=-zd end
- end
- --shader follows
- PointShader={
- vertexShader=[[
- uniform mat4 modelViewProjection;
- uniform mat4 mModel;
- uniform vec4 directColor;
- uniform vec3 directDirection;
- uniform vec4 ambientColor;
- uniform vec3 point1Position;
- uniform vec4 point1Color;
- attribute vec4 position;
- attribute vec2 texCoord;
- attribute vec3 normal;
- varying highp vec2 vTexCoord;
- varying lowp vec4 vPosition;
- varying lowp vec4 vNormal;
- varying vec4 vAmbientDiffuse;
- varying vec4 vPoint1Diffuse;
- void main()
- {
- vTexCoord = texCoord;
- vPosition = mModel * position;
- gl_Position = modelViewProjection * position;
- vNormal = mModel * vec4(normal, 0.0);
- //calculate diffuse directional light
- vec3 norm = normalize(vNormal.xyz);
- vAmbientDiffuse = ambientColor+directColor*max(0.0, dot(norm, directDirection));
- //calculate diffuse point light
- vec3 d = normalize( point1Position - vPosition.xyz );
- vPoint1Diffuse = point1Color * max(0.0, dot(norm, d));
- }
- ]],
- fragmentShader=[[
- precision highp float;
- uniform lowp sampler2D texture;
- uniform vec4 directColor;
- uniform vec3 directDirection;
- uniform vec3 eyePosition;
- uniform float specularPower;
- uniform float shine;
- uniform vec3 point1Position;
- uniform float point1Range;
- uniform vec4 point1Color;
- varying highp vec2 vTexCoord;
- varying lowp vec4 vPosition;
- varying lowp vec4 vNormal;
- varying vec4 vAmbientDiffuse;
- varying vec4 vPoint1Diffuse;
- vec3 norm = normalize(vNormal).xyz;
- vec4 specColor = min(directColor + 0.5, 1.0) * shine;
- void main()
- {
- vec3 cameraDirection = normalize(eyePosition - vPosition.xyz);
- //calculate specular light for directional light
- vec3 halfAngle = normalize(cameraDirection + directDirection);
- vec4 specDirect = specColor*pow(max(0.0, dot(norm, halfAngle)),specularPower);
- //calculate specular light for point light
- halfAngle = normalize(cameraDirection + normalize(point1Position - vPosition.xyz));
- vec4 specPoint = specColor*pow(max(0.0, dot(norm, halfAngle)),specularPower);
- //calculate strength of point light
- float point1Strength = max(0.0, 1.0-length(point1Position-vPosition.xyz) / point1Range);
- //add it up and apply to texture pixel
- vec4 col=texture2D(texture, vTexCoord);
- col=col*(vAmbientDiffuse+specDirect+point1Strength * (vPoint1Diffuse+specPoint));
- col.a= 1.;
- gl_FragColor=col;
- }
- ]]
- }
- --EVERYTHING BELOW IS UNCHANGED --
- function HandleTouches()
- modelMatrix(currentModelMatrix) --apply the stored settings
- --do rotation for touch
- if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
- rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
- rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
- currentModelMatrix = modelMatrix() --store the resulting settings for next time
- end
- end
- function CreateBlock(w,h,d,tex,col,pos,ms) --width,height,depth,texture,colour,position,mesh (if existing already)
- pos=pos or vec3(0,0,0)
- local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2
- local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)}
- local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7],
- v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]}
- local texCoords
- if tex then
- local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)}
- texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],
- t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]}
- end
- local n={vec3(0,0,1),vec3(1,0,0),vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0),vec3(0,-1,0)}
- local norm={}
- for i=1,6 do for j=1,6 do norm[#norm+1]=n[i] end end
- if not ms then ms=mesh() end
- if ms.size==0 then
- ms.vertices=vert
- ms.normals=norm
- ms.texture,ms.texCoords=tex,texCoords
- else
- for i=1,#vert do
- table.insert(ms.vertices,vert[i])
- table.insert(ms.normals,norm[i])
- if tex then table.insert(ms.texCoords,texCoords[i]) end
- end
- end
- ms:setColors(col or color(255))
- return ms
- end
- function PrintExplanation()
- output.clear()
- print("A point light is at a specific place in the scene and usually has limited range. \n\nRotate the cube with your finger to see the effect.")
- end
- --# Spot
- --Spot
- --[[
- A spot light is like a point light, except that the light doesn't shine in all directions. A spot light is like - well a spotlight, or torch, with a radius for the beam of light.
- The shader needs two extra items, the direction in which the light is pointing, and the radius of the beam
- The demo includes an optional extra, a flicker, which is not part of the shader. Flickering is produced simply
- by varying the visibility of the spot light using the noise function (see the UpdateShader function).
- --]]
- function setup()
- block=CreateBlock(1,0.5,0.75,"Platformer Art:Block Brick")
- currentModelMatrix=modelMatrix() --this is used for touches, not lighting
- camPos=vec3(0,0,2)
- z,zd=0,-.01
- SetupShader()
- end
- --All the lighting code is kept in these two functions to make it easier for you to see it and use it yourself
- function SetupShader() --set up the lighting
- --set position of light source
- block.shader=shader(SpotShader.vertexShader,SpotShader.fragmentShader)
- block.shader.ambientColor = color(255) *0.1
- block.shader.directColor=color(255,255,0) *0
- block.shader.directDirection = vec3(-1,0,1):normalize()
- block.shader.shine=0 --set this to 0 if you don't want specular lighting for your directional light
- block.shader.specularPower=32
- --point light variables needed by the shader --- ****** NEW
- block.shader.point1Position=vec3(0,0,2) --this is an actual position to the right of us
- block.shader.point1Color=color(255) *0.7 --multiply by a factor (eg 0.5) if you want to reduce brightness
- block.shader.point1Range=20 --light reduces to nil over this distance
- block.shader.point1Direction=vec3(0,0,-1):normalize()
- block.shader.spotAngle=5 --width of light beam in degrees
- parameter.number("Flicker",0,0.6,0)
- end
- function UpdateShader() --update the lighting details before drawing
- block.shader.mModel=modelMatrix()
- block.shader.eyePosition=camPos
- block.shader.point1Range=20*(1-Flicker+noise(ElapsedTime*5)*Flicker)
- end
- function draw()
- background(0)
- perspective()
- camera(camPos.x,camPos.y,camPos.z,0,0,0)
- currentModelMatrix[15]=z
- HandleTouches()
- UpdateShader()
- block:draw()
- z=z+zd
- if z<-6 or z>0 then zd=-zd end
- end
- --shader follows
- SpotShader={
- vertexShader=[[
- uniform mat4 modelViewProjection;
- uniform mat4 mModel;
- uniform vec4 directColor;
- uniform vec3 directDirection;
- uniform vec4 ambientColor;
- uniform vec3 point1Position;
- uniform vec4 point1Color;
- attribute vec4 position;
- attribute vec2 texCoord;
- attribute vec3 normal;
- varying highp vec2 vTexCoord;
- varying lowp vec4 vPosition;
- varying lowp vec4 vNormal;
- varying vec4 vAmbientDiffuse;
- varying vec4 vPoint1Diffuse;
- void main()
- {
- vTexCoord = texCoord;
- vPosition = mModel * position;
- gl_Position = modelViewProjection * position;
- vNormal = mModel * vec4(normal, 0.0);
- //calculate diffuse light
- vec3 norm = normalize(vNormal.xyz);
- vAmbientDiffuse = ambientColor+directColor*max(0.0, dot(norm, directDirection));
- vec3 d = normalize(point1Position - vPosition.xyz);
- vPoint1Diffuse = point1Color * max(0.0, dot(norm, d));
- }
- ]],
- fragmentShader=[[
- precision highp float;
- uniform lowp sampler2D texture;
- uniform vec4 directColor;
- uniform vec3 directDirection;
- uniform vec3 eyePosition;
- uniform float specularPower;
- uniform float shine;
- uniform vec3 point1Position;
- uniform float point1Range;
- uniform vec4 point1Color;
- uniform vec3 point1Direction;
- uniform float spotAngle;
- varying highp vec2 vTexCoord;
- varying lowp vec4 vPosition;
- varying lowp vec4 vNormal;
- varying vec4 vAmbientDiffuse;
- varying vec4 vPoint1Diffuse;
- vec3 norm = normalize(vNormal).xyz;
- vec4 specColor = min(directColor + 0.5, 1.0) * shine;
- float radius = cos(radians(spotAngle));
- void main()
- {
- vec3 cameraDirection = normalize(eyePosition - vPosition.xyz);
- //calculate specular light for directional light
- vec3 halfAngle = normalize(cameraDirection + directDirection);
- vec4 specDirect = specColor*pow(max(0.0, dot(norm, halfAngle)),specularPower);
- //calculate specular light for point light
- halfAngle = normalize(cameraDirection + normalize(point1Position - vPosition.xyz));
- vec4 specPoint = specColor*pow(max(0.0, dot(norm, halfAngle)),specularPower);
- //calculate strength of point light
- float point1Strength = max(0.0, 1.0-length(point1Position-vPosition.xyz) / point1Range);
- //calculate if pixel is within light radius
- float fracSpot;
- float cos = dot(point1Direction, normalize(vPosition.xyz - point1Position));
- if (cos>=radius && cos<=1.0) fracSpot = max(0.0, 1.0 - (1.0 - cos) / (1.0 - radius));
- else fracSpot = 0.0;
- //add it up and apply to texture pixel
- vec4 col=texture2D(texture, vTexCoord);
- col=col*(vAmbientDiffuse+specDirect+point1Strength * fracSpot * (vPoint1Diffuse+specPoint));
- col.a= 1.;
- gl_FragColor=col;
- }
- ]]
- }
- --EVERYTHING BELOW IS UNCHANGED --
- function HandleTouches()
- modelMatrix(currentModelMatrix) --apply the stored settings
- --do rotation for touch
- if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
- rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
- rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
- currentModelMatrix = modelMatrix() --store the resulting settings for next time
- end
- end
- function CreateBlock(w,h,d,tex,col,pos,ms) --width,height,depth,texture,colour,position,mesh (if existing already)
- pos=pos or vec3(0,0,0)
- local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2
- local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)}
- local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7],
- v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]}
- local texCoords
- if tex then
- local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)}
- texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],
- t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]}
- end
- local n={vec3(0,0,1),vec3(1,0,0),vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0),vec3(0,-1,0)}
- local norm={}
- for i=1,6 do for j=1,6 do norm[#norm+1]=n[i] end end
- if not ms then ms=mesh() end
- if ms.size==0 then
- ms.vertices=vert
- ms.normals=norm
- ms.texture,ms.texCoords=tex,texCoords
- else
- for i=1,#vert do
- table.insert(ms.vertices,vert[i])
- table.insert(ms.normals,norm[i])
- if tex then table.insert(ms.texCoords,texCoords[i]) end
- end
- end
- ms:setColors(col or color(255))
- return ms
- end
- function PrintExplanation()
- output.clear()
- print("A spot light is at a specific place in the scene, points in a specific direction, and usually has limited range. \n\nThis demo also shows how to add flicker (vary it with the parameter above).\n\nRotate the cube with your finger to see the effect.")
- end
- --# Main
- -- MultiStep
- function setup()
- steps = listProjectTabs()
- if steps[1]=="Notes" then table.remove(steps,1) end --remove first tab if named Notes
- table.remove(steps) --remove the last tab
- startStep()
- global = "select a step"
- end
- function showList()
- output.clear()
- for i=1,#steps do print(i,steps[i]) end
- end
- function startStep()
- resetMatrix()
- if cleanup then cleanup() end
- lastStep=Step or readProjectData("lastStep") or 1
- lastStep=math.min(lastStep,#steps)
- saveProjectData("lastStep",lastStep)
- parameter.clear()
- parameter.integer("Step", 1, #steps, lastStep,showList)
- parameter.action("Run", startStep)
- loadstring(readProjectTab(steps[Step]))()
- if PrintExplanation then PrintExplanation() end
- setup()
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement