webgl实现三维3d洞穴效果代码
代码语言:html
所属分类:三维
代码描述:webgl实现三维3d洞穴效果代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> </head> <body> <!-- partial:index.partial.html --> <canvas id="webgl" width="500" height="1758"></canvas> <script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_position; uniform mat4 u_modelViewMatrix; uniform mat4 u_projectionMatrix; void main() { gl_Position = a_position; } </script> <script id="fragmentShader" type="x-shader/x-fragment"> precision highp float; precision highp int; uniform vec2 u_resolution; uniform vec2 u_mouse; uniform float u_time; uniform sampler2D u_noise; // movement variables vec3 movement = vec3(.0); const int maxIterations = 256; const float stopThreshold = 0.002; const float stepScale = .5; const float eps = 0.002; const vec3 clipColour = vec3(1.); const vec3 fogColour = vec3(1.); const vec3 light1_position = vec3(0, 1., -1.); const vec3 light1_colour = vec3(.8, .8, .85); struct Surface { int object_id; float distance; vec3 position; vec3 onsurface_position; vec3 colour; float steps; float ambient; float spec; float fog; }; // Distance function copyright Inigo Quilez float opExtrusion( in vec3 p, in float primitive, in float h ) { float d = primitive; vec2 w = vec2( d, abs(p.z) - h ); return min(max(w.x,w.y),0.0) + length(max(w,0.0)); } float sdBox( in vec2 p, in vec2 b ) { vec2 d = abs(p)-b; return length(max(d,0.0)) + min(max(d.x,d.y),0.0); } vec3 path(float z) { // return vec3(0,0,0.); return vec3(sin(z * .1) * 4., sin(z * .05) * 10., z); } float getBlock(vec3 position, inout int object_id, inout vec3 p) { // position.z = fract(position.z) - .5; // object_id = int(id); float id = float(object_id); float r = sin(id * .3 + u_time) * .5; r = sin(texture2D(u_noise, vec2((id*2.)/255.)).x * 2. + u_time - id) * .25; float rw = texture2D(u_noise, vec2((id*2.)/255.)).x - .5; // // position.xy += r; float s = sin(r); float c = cos(r); position.xy *= mat2(c, -s, s, c); p = position; float box = sdBox(position.xy, vec2(1.5 + rw, 1.)) * -1.; float world = opExtrusion(position, box, .45) - .005; // world += smoothstep(.04, 0., abs(sin(p.x * 4.))) * .01; // world += smoothstep(.02, 0., abs(sin(p.y * 2.))) * .01; return world; } // This function describes the world in distances from any given 3 dimensional point in space float world(in vec3 position, inout int object_id, inout vec3 p) { position.xy -= path(position.z).xy; float id = floor(position.z); position.z = fract(position.z) - .5; int oid1 = int(id); vec3 p1; float block1 = getBlock(position, oid1, p1); int oid2 = int(id+1.); vec3 p2; float block2 = getBlock(position + vec3(0,0,-1), oid2, p2); object_id = oid1; p = p1; if(block2 < block1) { block1 = block2; object_id = oid2; p = p2; } return block1; } float world(in vec3 position, inout int object_id) { vec3 p; return world(position, object_id, p); } float world(in vec3 position) { int dummy = 0; return world(position, dummy); } Surface getSurface(int object_id, float rayDepth, vec3 sp, float steps, vec3 onsurface_pos, float fog) { return Surface( object_id, rayDepth, sp, onsurface_pos, vec3(1.), steps, .5, 1000., fog); } // The raymarch loop Surface rayMarch(vec3 ro, vec3 rd, float start, float end) { float sceneDist = 1e4; float rayDepth = start; int object_id = 0; float steps = 0.; float fog = 0.; vec3 p; for(int i = 0; i < maxIterations; i++) { sceneDist = world(ro + rd * rayDepth, object_id, p); rd += (texture2D(u_noise, (p.xy)*255.).rgb-.5)*.0005; steps++; fog += max(sceneDist, 0.); if(sceneDist < stopThreshold || rayDepth > end) { break; } rayDepth += sceneDist * stepScale; } return getSurface(object_id, rayDepth, ro + rd * rayDepth, steps, p, fog); } // Calculated the normal of any given point in space. Intended to be cast from the point of a surface vec3 calculate_normal(in vec3 position) { vec3 grad = vec3( world(vec3(position.x + eps, position.y, position.z)) - world(vec3(position.x - eps, position.y, position.z)), world(vec3(position.x, position.y + eps, position.z)) - world(vec3(position.x, position.y - eps, position.z)), world(vec3(position.x, position.y, position.z + eps)) - world(vec3(position.x, position.y, position.z - eps)) ); return normalize(grad); } vec3 lighting(Surface surface_object, vec3 cam) { // start with black vec3 sceneColour = vec3(0); // Surface normal vec3 normal = calculate_normal(surface_object.position); normal += smoothstep(.02, 0., abs(sin(surface_object.onsurface_position.x * 4.))) * .5; normal += smoothstep(.01, 0., abs(sin(surface_object.onsurface_position.y * 2.))) * .5; // Light position vec3 lp = path(u_time*3.+15.); // Light direction vec3 ld = lp - surface_object.position; // light attenuation // For brightly lit scenes or global illumination (like sunlit), this can be limited to just normalizing the ld float len = length( ld ); ld = normalize(ld); float lightAtten = min( 1.0 / ( 0.15*len ), 1.0 ); lightAtten = 1.; // The surface's light reflection normal vec3 reflection_normal = reflect(-ld, normal); // Ambient Occlusion float ao = (surface_object.steps*.01*(1./(surface_object.fog*.07))); ao *= ao*2.; ao = clamp( ((1.-ao)+.3) , 0., 1.); // ao -= surface_object.steps*.005; // Object surface properties float diffuse = max(0., dot(normal, ld)); float specular = max(0., dot( reflection_normal, normalize(cam - surface_object.position) )); // Bringing all of the lighting components together vec3 tp = surface_object.onsurface_position * 2.; vec3 c = texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr; tp = surface_object.onsurface_position * vec3(.8, .8, 1.); c += texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr; tp = surface_object.onsurface_position * vec3(1., .4, .4); c += texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr; c *= .125; c *= .5 + .5; c *= vec3(1,.98,.90); sceneColour += ( c * (diffuse + specular )) * light1_colour * lightAtten * ao; // adding fog float fogl = surface_object.fog*.04; fogl *= smoothstep(-.5, 1.8, fogl); sceneColour = mix( sceneColour, fogColour, fogl ); return sceneColour; } void main() { vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x); float t = u_time * 3.; // movement movement = path(t); // Camera and look-at vec3 cam = vec3(0,0,-2); vec3 lookAt = vec3(0,-.5,-2.); // add movement lookAt = path(t+3.); cam = movement; // cam.y += abs(sin(u_time*20.)*.02); // Unit vectors vec3 forward = normalize(lookAt - cam); vec3 right = normalize(vec3(forward.z, 0., -forward.x)); vec3 up = normalize(cross(forward, right)); // FOV float FOV = 1.4; // Ray origin and ray direction vec3 ro = cam; vec3 rd = normalize(forward + FOV * uv.x * right + FOV * uv.y * up); rd.y -= (movement.y - lookAt.y) * .04; // Ray marching const float clipNear = 0.; const float clipFar = 20.; Surface objectSurface = rayMarch(ro, rd, clipNear, clipFar); if(objectSurface.distance > clipFar) { gl_FragColor = vec4(clipColour, 1.); return; } vec3 sceneColour = lighting(objectSurface, cam); gl_FragColor = vec4(sceneColour, 1.); // gl_FragColor += vec4(vec3(objectSurface.fog*.01), 1.); } </script> <!-- partial --> <script > /** * A basic Web GL class. This provides a very basic setup for GLSL shader code. * Currently it doesn't support anything except for clip-space 3d, but this was * done so that we could start writing fragments right out of the gate. My * Intention is to update it with particle and polygonal 3d support later on. * * @class WTCGL * @author Liam Egan <liam@wethecollective.com> * @version 0.0.8 * @created Jan 16, 2019 */ class WTCGL { /** * The WTCGL Class constructor. If construction of the webGL context fails * for any reason this will return null. * * @TODO make the dimension properties properly optional * @TODO provide the ability to allow for programmable buffers * * @constructor * @param {HTMLElement} el The canvas element to use as the root * @param {string} vertexShaderSource The vertex shader source * @param {string} fragmentShaderSource The fragment shader source * @param {number} [width] The width of the webGL context. This will default to the canvas dimensions * @param {number} [height] The height of the webGL context. This will default to the canvas dimensions * @param {number} [pxratio=1] The pixel aspect ratio of the canvas * @param {boolean} [styleElement] A boolean indicating whether to apply a style property to the canvas (resizing the canvas by the inverse of the pixel ratio) * @param {boolean} [webgl2] A boolean indicating whether to try to create a webgl2 context instead of a regulart context */ constructor(el, vertexShaderSource, fragmentShaderSource, width, height, pxratio, styleElement, webgl2) { this.run = this.run.bind(this); this._onRun = () => {}; // Destructure if an object is aprovided instead a series of parameters if (el instanceof Object && el.el) { ({ el, vertexShaderSource, fragmentShaderSource, width, height, pxratio, webgl2, styleElement } = el); } // If the HTML element isn't a canvas, return null if (!el instanceof HTMLElement || el.nodeName.toLowerCase() !== 'canvas') { console.log('Provided element should be a canvas element'); return null; } this._el = el; // The context should be either webgl2, webgl or experimental-webgl if (webgl2 === true) { this.isWebgl2 = true; this._ctx = this._el.getContext("webgl2", this.webgl_params) || this._el.getContext("webgl", this.webgl_params) || this._el.getContext("experimental-webgl", this.webgl_params); } else { this.isWebgl2 = false; this._ctx = this._el.getContext("webgl", this.webgl_params) || this._el.getContext("experimental-webgl", this.webgl_params); } // Set up the extensions this._ctx.getExtension('OES_standard_derivatives'); this._ctx.getExtension('EXT_shader_texture_lod'); this._ctx.getExtension('OES_texture_float'); this._ctx.getExtension('WEBGL_color_buffer_float'); this._ctx.getExtension('OES_texture_float_linear'); this._ctx.getExtension('EXT_color_buffer_float'); // We can't make the context so return an error if (!this._ctx) { console.log('Browser doesn\'t support WebGL '); return null; } // Create the shaders this._vertexShader = WTCGL.createShaderOfType(this._ctx, this._ctx.VERTEX_SHADER, vertexShaderSource); this._fragmentShader = WTCGL.createShaderOfType(this._ctx, this._ctx.FRAGMENT_SHADER, fragmentShaderSource); // Create the program and link the shaders this._program = this._ctx.createProgram(); this._ctx.attachShader(this._program, this._vertexShader); this._ctx.attachShader(this._program, this._fragmentShader); this._ctx.linkProgram(this._program); // If we can't set up the params, this means the shaders have failed for some reason if (!this._ctx.getProgramParameter(this._program, this._ctx.LINK_STATUS)) { console.log('Unable to initialize the shader program: ' + this._ctx.getProgramInfoLog(this._program)); return null; } // Initialise the vertex buffers this.initBuffers([ -1.0, 1.0, -1., 1.0, 1.0, -1., -1.0, -1.0, -1., 1.0, -1.0, -1.]); // Initialise the frame buffers this.frameBuffers = []; // The program information object. This is essentially a state machine for the webGL instance this._programInfo = { attribs: { vertexPosition: this._ctx.getAttribLocation(this._program, 'a_position') }, uniforms: { projectionMatrix: this._ctx.getUniformLocation(this._program, 'u_projectionMatrix'), modelViewMatrix: this._ctx.getUniformLocation(this._program, 'u_modelViewMatrix'), resolution: this._ctx.getUniformLocation(this._program, 'u_resolution'), time: this._ctx.getUniformLocation(this._program, 'u_time') } }; // Tell WebGL to use our program when drawing this._ctx.useProgram(this._program); this.pxratio = pxratio; this.styleElement = styleElement !== true; this.resize(width, height); } /** * Public methods */ addFrameBuffer(w, h, tiling = 0, buffertype = 0) { // create to render to const gl = this._ctx; const targetTextureWidth = w * this.pxratio; const targetTextureHeight = h * this.pxratio; const targetTexture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, targetTexture); { // define size and format of level 0 const level = 0; let internalFormat = gl.RGBA; const border = 0; let format = gl.RGBA; let t; if (buffertype & WTCGL.TEXTYPE_FLOAT) { const e = gl.getExtension('OES_texture_float'); window.extension = e; t = e.FLOAT; // internalFormat = gl.RGBA32F; } else if (buffertype & WTCGL.TEXTYPE_HALF_FLOAT_OES) { // t = gl.renderer.isWebgl2 ? e.HALF_FLOAT : e.HALF_FLOAT_OES; // gl.renderer.extensions['OES_texture_half_float'] ? gl.renderer.extensions['OES_texture_half_float'].HALF_FLOAT_OES : // gl.UNSIGNED_BYTE; const e = gl.getExtension('OES_texture_half_float'); t = this.isWebgl2 ? gl.HALF_FLOAT : e.HALF_FLOAT_OES; // format = gl.RGBA; if (this.isWebgl2) { internalFormat = gl.RGBA16F; } // internalFormat = gl.RGB32F; // format = gl.RGB32F; // window.gl = gl // t = e.HALF_FLOAT_OES; } else { t = gl.UNSIGNED_BYTE; } const type = t; const data = null; gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, targetTextureWidth, targetTextureHeight, border, format, type, data); // gl.generateMipmap(gl.TEXTURE_2D); // set the filtering so we don't need mips gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); // Set the parameters based on the passed type if (tiling === WTCGL.IMAGETYPE_TILE) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } else if (tiling === WTCGL.IMAGETYPE_MIRROR) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); } else if (tiling === WTCGL.IMAGETYPE_REGULAR) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } } // Create and bind the framebuffer const fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fb); // attach the texture as the first color attachment const attachmentPoint = gl.COLOR_ATTACHMENT0; const level = 0; gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level); return { w: w * this.pxratio, h: h * this.pxratio, fb: fb, frameTexture: targetTexture }; } /** * Resizes the canvas to a specified width and height, respecting the pixel ratio * * @param {number} w The width of the canvas * @param {number} h The height of the canvas * @return {Void} */ resize(w, h) { this.width = w; this.height = h; this._el.width = w * this.pxratio; this._el.height = h * this.pxratio; this._size = [w * this.pxratio, h * this.pxratio]; if (this.styleElement) { this._el.style.width = w + 'px'; this._el.style.height = h + 'px'; } this._ctx.viewportWidth = w * this.pxratio; this._ctx.viewportHeight = h * this.pxratio; this._ctx.uniform2fv(this._programInfo.uniforms.resolution, this._size); this.initBuffers(this._positions); } /** * Initialise a provided vertex buffer * * @param {array} positions The vertex positions to initialise * @return {Void} */ initBuffers(positions) { this._positions = positions; this._positionBuffer = this._ctx.createBuffer(); this._ctx.bindBuffer(this._ctx.ARRAY_BUFFER, this._positionBuffer); this._ctx.bufferData(this._ctx.ARRAY_BUFFER, new Float32Array(positions), th.........完整代码请登录后点击上方下载按钮下载查看
网友评论0