Skip to content

Instantly share code, notes, and snippets.

@sp4cemonkey
Last active December 14, 2015 11:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sp4cemonkey/5078136 to your computer and use it in GitHub Desktop.
Save sp4cemonkey/5078136 to your computer and use it in GitHub Desktop.
Vintage 3d Tile Based w lighting and bump mapping
--# Main
--# Main
-- Use this function to perform your initial setup
displayMode(FULLSCREEN)
function setup()
stick = Stick()
pylon = Wall("Cargo Bot:Crate Red 2")
wall = Wall("Cargo Bot:Crate Yellow 2")
floor = Floor("Cargo Bot:Crate Green 2")
world = World()
hero = Hero(3,3)
TO_DEG = 180/math.pi
worldRenderImage = image(WIDTH/2, HEIGHT/2)
filmEffect = Vintage(worldRenderImage, WIDTH, HEIGHT)
end
-- This function gets called once every frame
function draw()
--output.clear()
print(1/DeltaTime)
--print(ElapsedTime)
setContext(worldRenderImage)
background(0)
local TO_DEG = TO_DEG
local hero = hero
perspective(60)
camera(hero.x, 3, 1 + hero.z, hero.x, 0, hero.z, 0, 1, 0)
eye = vec3(hero.x, 3, 1 + hero.z)
--fixed light
--light = vec3(7, 3, 7)
--light just above the hero's head
light = vec3(hero.x, 0.5, hero.z)
-- Draw world
pushMatrix()
world:draw(eye, light)
popMatrix()
-- Draw hero
translate(hero.x, hero.y, hero.z)
rotate(stick.direction*TO_DEG, 0, 1, 0)
-- roll animation
if stick.active then
rotate(-ElapsedTime*2*TO_DEG, 0, 0, 1)
end
scale(.25, .25, .25)
hero:draw(eye, light)
-- Restore orthographic projection
ortho()
viewMatrix(matrix())
resetMatrix()
setContext()
background(0)
filmEffect:draw()
-- fade out overlay
--sprite("Cargo Bot:Background Fade", WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
if stick.active then
local ceil = math.ceil
stick:draw()
-- move hero based on stick direction
local mvtx = math.cos(stick.direction)/50*stick.dist
local mvtz = -math.sin(stick.direction)/50*stick.dist
hero.x = hero.x + mvtx
hero.z = hero.z + mvtz
-- convert to table coordinates
hero.px = ceil(hero.x - .5)
hero.py = ceil(hero.z - .5)
-- lazy collision check
if world.data[hero.py][hero.px] ~= 0 then
hero.x = hero.x - mvtx
hero.z = hero.z - mvtz
hero.px = ceil(hero.x - .5)
hero.py = ceil(hero.z - .5)
end
end
end
function touched(touch)
stick:touched(touch)
end
--# World
World = class()
function World:init()
-- define the world
self.data =
{
{1, 1, 1, 1, 1, 1, 1, 1},
{1, 2, 0, 0, 0, 0, 2, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 1, 2, 0, 0, 1},
{1, 0, 0, 2, 1, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 2, 0, 0, 0, 0, 2, 1},
{1, 1, 1, 1, 1, 1, 1, 1}
}
end
function World:draw(eye, light)
local floor, wall, pylon = floor, wall, pylon
local offSet = 3
local px, py = hero.px, hero.py
-- look around the hero to draw whatever is around him
translate(px - offSet, 0, py - offSet)
for y = py - offSet, py + offSet do
for x = px - offSet, px + offSet do
if self.data[y] then
local val = self.data[y][x]
if val == 0 then
floor:draw(eye, light)
elseif val == 1 then
wall:draw(eye, light)
elseif val == 2 then
pylon:draw(eye, light)
end
end
translate(1,0,0)
end
translate(-(1 + 2 * offSet), 0, 1)
end
end
--# Hero
Hero = class()
function Hero:init(x, z)
self.x, self.y, self.z = x,0,z
self.px, self.py = math.ceil(.5+x), math.ceil(.5+z)
self.mdl = Wall("Cargo Bot:Crate Blue 2")
end
function Hero:draw(eye, light)
self.mdl:draw(eye, light)
end
--# Wall
Wall = class()
function Wall:init(tex)
-- all the unique vertices that make up a cube
local vertices =
{
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
local verts =
{
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1]
}
-- all the unique texture positions needed
local texvertices =
{
vec2(0.02,0.02),
vec2(0.98,0.02),
vec2(0.02,0.98),
vec2(0.98,0.98)
}
-- apply the texture coordinates to each triangle
local texCoords =
{
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3]
}
self.model = LitMesh()
self.model.litMesh.vertices = verts
theTexture = readImage(tex)
self.model.litMesh.texCoords = texCoords
self.model:deriveVertexNTB()
self.model:setTexture(theTexture)
local tempText = readImage(tex)
theBumpMap = self.model:generateTextureBumpMap(tempText,0.007)
self.model:setBumpMap(theBumpMap)
end
function Wall:draw(eye, light)
self.model:setLight(light, 0.2, 1.0, 1.0, color(255))
self.model:setEye(eye)
self.model:draw()
end
--# Floor
Floor = class()
function Floor:init(tex)
-- all the unique vertices that make up a cube
local vertices =
{
vec3( 0.5, -0.5, 0.5), -- Right top front
vec3(-0.5, -0.5, 0.5), -- Left top front
vec3( 0.5, -0.5, -0.5), -- Right top back
vec3(-0.5, -0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
local verts =
{
-- Bottom
vertices[3], vertices[4], vertices[2],
vertices[3], vertices[2], vertices[1],
}
-- all the unique texture positions needed
local texvertices =
{
vec2(0.02,0.02),
vec2(0.98,0.02),
vec2(0.02,0.98),
vec2(0.98,0.98)
}
-- apply the texture coordinates to each triangle
local texCoords =
{
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
self.model = LitMesh()
self.model.litMesh.vertices = verts
theTexture = readImage(tex)
self.model.litMesh.texCoords = texCoords
self.model:deriveVertexNTB()
self.model:setTexture(theTexture)
local tempText = readImage(tex)
theBumpMap = self.model:generateTextureBumpMap(tempText,0.007)
self.model:setBumpMap(theBumpMap)
end
function Floor:draw(eye, light)
self.model:setLight(light, 0.2, 1.0, 0.6, color(255))
self.model:setEye(eye)
self.model:draw()
end
--# Stick
Stick = class()
function Stick:init()
self.direction = 0
self.dist = 0
self.active = false
self.origin = vec2(150, 150)
self.center = self.origin
self.pos = self.origin
self.stick_bg = readImage("Small World:Dialog Icon")
self.stick = readImage("Small World:Bush")
-- self.stick_bg = readImage("Space Art:Eclipse")
-- self.stick = readImage("Space Art:UFO")
end
function Stick:draw()
sprite(self.stick_bg, self.center.x, self.center.y)
sprite(self.stick, self.pos.x, self.pos.y)
end
function Stick:touched(touch)
if touch.state == BEGAN then
self.center = vec2(touch.x, touch.y)
self.active = true
end
self.pos = vec2(touch.x, touch.y)
self.direction = math.atan2(self.pos.y - self.center.y, self.pos.x - self.center.x)
self.dist = math.min(2, self.pos:dist(self.center)/32)
if touch.state == ENDED then
self.center = self.origin
self.pos = self.center
self.active = false
end
end
--# LitMesh
LitMesh = class()
--[[
LitMesh provides a bumpmappable ADS (ambient/diffuse/specular) lighting enhanced class
for meshes.
usage:
setup()
myObject = LitMesh()
myObject.litMesh.vertices = some table of vertices
-- this must be a multiple of 3 representing the triangles
-- this is the normal mesh vertices array and can be manipulated as per docs
-- eg myObject.litMesh:vertex(i, vec3(x,y,z))
-- myObject.litMesh:setRect(i,x,y,w,h)
myObject.litMesh.texCoords = some table of texture coordinates for the vertices
-- this is the normal mesh texCoords array and can be
manipulated as per the docs
-- eg myObject.litMesh:texCoord(i, x, y)
-- myObject.litMesh:setRectTex(i,s,t,w,h)
--Once all vertexes and texture coordinates have been set call:
myObject:deriveVertexNTB()
-- you will need to make this call again if you modify any vertexes or texture coordinates
--setup lighting
--lightPosition is the location of the light in world space
--eyePosition is the location of the camera in world space this is the eye vector in your camera() call
myObject:setLight(vec3(lightPosition), ambient, diffuse, specular, lightColor)
--set the eye position which is the eye vector from your camera statement
myObject:setEye(vec3(eyePosition))
--then either color or texture your mesh with one of
myObject:setColor(color)
myObject:setTexture(texture)
--finally if you wish to use bumpmapping set a bumpmap
myObject:setBumpMap(bumpMap)
--a bumpMap is just an image with normal vectors encoded into the colors, these can be generated in tools like blender or photoshop
--Alternately the LitMesh class contains (a poor performing) helper method for generating bumpMaps from your texture: - strength is how aggressive the edge mapping is 0.01 is a good start
myObject:generateTextureBumpMap(texture, strength)
--this could be used as a shortcut by:
bumpTexture = cube:generateTextureBumpMap(cubeTexture, 0.01)
myObject:setBumpMap(bumpTexture)
draw()
--do all your normal drawing activity
background(40, 40, 50)
camera(eyex, eyey, eyez, lookatx, lookaty, lookatz)
--if the camera eye is moving remember to do
myObject:setEye(vec3(eyex, eyey, eyez))
perspective()
--object drawing
pushMatrix()
translate(x,y,z) --move centre of your mesh wherever
rotate(angle,x,y,z) -- rotate your object about it's centre
myObject:draw()
popMatrix()
]]
function LitMesh:init()
self.litMesh = mesh()
self.litMesh.shader = shader(LitMesh.ADSLighting.vertexShader, LitMesh.ADSLighting.fragmentShader)
self.litMesh.shader.useTexture = false
self.litMesh.shader.useBumpMap = false
end
function LitMesh:draw()
self.litMesh.shader.mInvModel = modelMatrix():inverse():transpose()
self.litMesh:draw()
end
function LitMesh:setLight(lightPosition, ambient, diffuse, specular, lightColor)
self.litMesh.shader.vLightPosition = lightPosition
self.litMesh.shader.vAmbientMaterial = ambient
self.litMesh.shader.vDiffuseMaterial = diffuse
self.litMesh.shader.vSpecularMaterial = specular
self.litMesh.shader.lightColor = lightColor
end
function LitMesh:setEye(eyePosition)
self.litMesh.shader.vEyePosition = eyePosition
end
function LitMesh:setTexture(texture)
self.litMesh.shader.useTexture = true
self.litMesh.texture = texture
end
function LitMesh:disableBumpMap()
self.litMesh.shader.useBumpMap = false
end
function LitMesh:setBumpMap(bumpMap)
self.litMesh.shader.useBumpMap = true
self.litMesh.shader.bumpMap = bumpMap
end
function LitMesh:setColor(surfaceColor)
--assumes the reason you are setting the color is because you don't want to use a texture
self.litMesh.texture = nil
self.litMesh.shader.useTexture = false
self.litMesh:setColors(surfaceColor)
end
function LitMesh:deriveVertexNTB()
--this will calculate the tangent, binormal and from those the normal for each vertex, these will all be stored in buffers
--assumes that the surfaces have their texture coordinates set (even if being left untextured)
--tangent is the X axis on the plane of the surface relative to textures
--binormal is the Y axis on the plane of the surface relative to textures
--normal is the surface normal
local texCoordBuffer = self.litMesh:buffer("texCoord")
if texCoordBuffer.length == self.litMesh.size then
useTexCoords = true
else
useTexCoords = false
end
local normalBuffer = self.litMesh:buffer("normal")
normalBuffer:resize(self.litMesh.size)
local tangentBuffer = self.litMesh:buffer("tangent")
tangentBuffer:resize(self.litMesh.size)
--local binormalBuffer = self.litMesh:buffer("binormal")
--binormalBuffer:resize(self.litMesh.size)
local tangent,binormal, normal
for i=1, self.litMesh.size/3 do
--calculate the surface vectors
local v1 = self.litMesh:vertex(i*3-1) - self.litMesh:vertex(i*3-2)
local v2 = self.litMesh:vertex(i*3) - self.litMesh:vertex(i*3-2)
--calculate the texture space vectors
if useTexCoords then
local tuV = vec2(self.litMesh:texCoord(i*3-1).x - self.litMesh:texCoord(i*3-2).x, self.litMesh:texCoord(i*3).x - self.litMesh:texCoord(i*3-2).x)
local tvV = vec2(self.litMesh:texCoord(i*3-1).y - self.litMesh:texCoord(i*3-2).y, self.litMesh:texCoord(i*3).y - self.litMesh:texCoord(i*3-2).y)
--calculate denominator
local den=1/(tuV.x*tvV.y - tuV.y*tvV.x)
--tangent
tangent = vec3((tvV.y*v1.x - tvV.x*v2.x)*den, (tvV.y*v1.y - tvV.x*v2.y)*den, (tvV.y*v1.z - tvV.x*v2.z)*den):normalize()
binormal = vec3((tuV.x*v2.x - tuV.y*v1.x)*den, (tuV.x*v2.y - tuV.y*v1.y)*den, (tuV.x*v2.z - tuV.y*v1.z)*den):normalize()
normal = tangent:cross(binormal):normalize()
else
tangent = v1:normalize()
normal = v1:normalize():cross(v2:normalize()):normalize()
binormal = normal:cross(tangent):normalize()
end
for j=i*3-2,i*3 do
normalBuffer[j] = normal
--binormalBuffer[j] = binormal
tangentBuffer[j] = tangent
end
end
end
function LitMesh:generateTextureBumpMap(source, strength)
local t = image(source.width, source.height)
for y=2,source.height-1 do
for x=2,source.width-1 do
r,g,b,a = source:get(x-1,y)
xLeft = (0.3*r+0.59*g+0.11*b)*strength
r,g,b,a = source:get(x+1,y)
xRight = (0.3*r+0.59*g+0.11*b)*strength
r,g,b,a = source:get(x,y-1)
yUp = (0.3*r+0.59*g+0.11*b)*strength
r,g,b,a = source:get(x,y+1)
yDown = (0.3*r+0.59*g+0.11*b)*strength
xDelta = ((xLeft-xRight)+1)*127.5
yDelta = ((yUp - yDown)+1)*127.5
t:set(x,y,color(xDelta,yDelta,255,255))
end
end
for y=1,source.height do
t:set(1,y,color(0,0,255,255))
t:set(source.width,y,color(0,0,255,255))
end
for x=1,source.width do
t:set(x,1,color(0,0,255,255))
t:set(x,source.height,color(0,0,255,255))
end
return t
end
LitMesh.ADSLighting = {
--shader()
vertexShader = [[
uniform lowp mat4 modelViewProjection;
uniform lowp mat4 mInvModel;
uniform lowp vec3 vEyePosition;
uniform lowp vec3 vLightPosition;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec3 normal;
attribute vec3 tangent;
varying lowp vec2 vTexCoord;
varying lowp vec3 lightDirection;
varying lowp vec3 eyeDirection;
void main()
{
lowp mat3 tangentMatrix = mat3(tangent, cross(normal, tangent), normal);
//convert the positions to object space, then get direction, then convert to tangent space
lightDirection = (((vec4(vLightPosition,1) * mInvModel) - position).xyz
* tangentMatrix).xyz;
eyeDirection = (((vec4(vEyePosition,1) * mInvModel) - position).xyz
* tangentMatrix).xyz;
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision lowp float;
uniform lowp sampler2D texture;
uniform lowp sampler2D bumpMap;
varying lowp vec2 vTexCoord;
varying lowp vec3 lightDirection;
varying lowp vec3 eyeDirection;
uniform float vAmbientMaterial;
uniform float vDiffuseMaterial;
uniform float vSpecularMaterial;
uniform lowp vec4 lightColor;
const float c_zero = 0.0;
const float c_one = 1.0;
const float c_two = 2.0;
void main()
{
if (!gl_FrontFacing) discard;
vec3 curNormal = normalize(texture2D( bumpMap, vTexCoord ).xyz*c_two-vec3(c_one,c_one,c_one));
lowp vec4 curCol = texture2D( texture, vTexCoord);
vec3 vLightDirection = normalize(normalize(lightDirection));
vec3 vCameraDirection = normalize(eyeDirection);
lowp vec4 vAmbientColor = curCol * lightColor * vAmbientMaterial;
// Calculate Diffuse intensity
float fDiffuseIntensity = max( c_zero, dot( curNormal, vLightDirection ));
lowp vec4 vDiffuseColor = curCol * lightColor * fDiffuseIntensity * vDiffuseMaterial;
// Calculate the reflection vector between the incoming light and the
// normal (incoming angle = outgoing angle)
vec3 vReflection = reflect( -vLightDirection, curNormal );
// Calculate specular component
// Based on the dot product between the reflection vector and the camera
// direction.
float spec = pow( max( c_zero, dot( vCameraDirection, vReflection )), 32.0 );
lowp vec4 vSpecularColor = lightColor * spec * vSpecularMaterial;
vAmbientColor.a = c_one;
vDiffuseColor.a = c_one;
vSpecularColor.a = c_one;
//Set the output color to the texture color
gl_FragColor = vAmbientColor + vDiffuseColor + vSpecularColor;
}
]]
}
--# Vintage
Vintage = class()
function Vintage:init(texture, tw, th)
self.mesh = mesh()
self.mesh.texture = texture
self.mesh.shader = shader(Vintage.FilmShader.FilmVertex, Vintage.FilmShader.FilmFragment)
--local tw,th = texture.width, texture.height
self.mesh:addRect(tw/2, th/2, tw, th)
-- configuration of the filter
self.mesh.shader.SepiaValue = 0.6
self.mesh.shader.NoiseValue = 0.2
self.mesh.shader.ScratchValue = 0.5
self.mesh.shader.InnerVignetting = 0.8
self.mesh.shader.OuterVignetting = 0.9
self.mesh.shader.RandomValue = math.random()
self.mesh.shader.TimeLapse = math.random()
self.frameRate = 10
self.timeSinceLastDraw = 0
end
function Vintage:draw()
self.timeSinceLastDraw = self.timeSinceLastDraw + DeltaTime
--even though redraw is as fast as possible, the noise etc will only shift when we do this update...
if self.timeSinceLastDraw > 1/self.frameRate then
self.mesh.shader.RandomValue = math.random()
self.timeSinceLastDraw = 0
if math.random() > 0.9 then
self.mesh.shader.TimeLapse = math.random()
end
end
-- Draw the mesh
self.mesh:draw()
end
Vintage.FilmShader = {
FilmVertex = [[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
//uniform bool isCamera;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
//This is an output variable that will be passed to the fragment shader
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
vTexCoord = texCoord;
//Camera isn't oriented correctly, so this code would fix it
//if (isCamera) {
// vTexCoord.y = 1.0 - vTexCoord.y;
//}
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * position;
}
]],
FilmFragment = [[
precision highp float;
/// Uniform variables.
uniform vec4 Colour;
uniform sampler2D texture;
uniform float SepiaValue;
uniform float NoiseValue;
uniform float ScratchValue;
uniform float InnerVignetting;
uniform float OuterVignetting;
uniform float RandomValue;
uniform float TimeLapse;
/// Varying variables.
varying vec2 vTexCoord;
/// Computes the overlay between the source and destination colours.
vec3 Overlay (vec3 src, vec3 dst) {
// if (dst <= Ω) then: 2 * src * dst
// if (dst > Ω) then: 1 - 2 * (1 - dst) * (1 - src)
return vec3((dst.x <= 0.5) ? (2.0 * src.x * dst.x) : (1.0 - 2.0 * (1.0 - dst.x) * (1.0 - src.x)), (dst.y <= 0.5) ? (2.0 * src.y * dst.y) : (1.0 - 2.0 * (1.0 - dst.y) * (1.0 - src.y)), (dst.z <= 0.5) ? (2.0 * src.z * dst.z) : (1.0 - 2.0 * (1.0 - dst.z) * (1.0 - src.z)));
}
/// 2D Noise by Ian McEwan, Ashima Arts.
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 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
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);
}
/// Fragment shader entry.
void main () {
// Sepia RGB value
vec3 sepia = vec3(112.0 / 255.0, 66.0 / 255.0, 20.0 / 255.0);
// Step 1: Convert to grayscale
vec3 colour = texture2D(texture, vTexCoord).xyz;
float gray = (colour.x + colour.y + colour.z) / 3.0;
vec3 grayscale = vec3(gray);
// Step 2: Appy sepia overlay
vec3 finalColour = Overlay(sepia, grayscale);
// Step 3: Lerp final sepia colour
finalColour = grayscale + SepiaValue * (finalColour - grayscale);
// Step 4: Add noise
float noise = snoise(vTexCoord * vec2(1024.0 + RandomValue * 512.0, 1024.0 + RandomValue * 512.0)) * 0.5;
finalColour += noise * NoiseValue;
// Optionally add noise as an overlay, simulating ISO on the camera
//vec3 noiseOverlay = Overlay(finalColour, vec3(noise));
//finalColour = finalColour + NoiseValue * (finalColour - noiseOverlay);
// Step 5: Apply scratches
if ( RandomValue < ScratchValue ) {
// Pick a random spot to show scratches
float dist = 1.0 / ScratchValue;
float d = distance(vTexCoord, vec2(RandomValue * dist, RandomValue * dist));
if ( d < 0.4 ) {
// Generate the scratch
float xPeriod = 8.0;
float yPeriod = 1.0;
float pi = 3.141592;
float phase = TimeLapse;
float turbulence = snoise(vTexCoord * 2.5);
float vScratch = 0.5 + (sin(((vTexCoord.x * xPeriod + vTexCoord.y * yPeriod + turbulence)) * pi + phase) * 0.5);
vScratch = clamp((vScratch * 10000.0) + 0.35, 0.0, 1.0);
finalColour.xyz *= vScratch;
}
}
// Step 6: Apply vignetting
// Max distance from centre to corner is ~0.7. Scale that to 1.0.
float d = distance(vec2(0.5, 0.5), vTexCoord) * 1.414213;
float vignetting = clamp((OuterVignetting - d) / (OuterVignetting - InnerVignetting), 0.0, 1.0);
finalColour.xyz *= vignetting;
// Apply colour
gl_FragColor.xyz = finalColour;
gl_FragColor.w = 1.0;
}
]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment