index.vert
  #version 300 es
void main() {
  float x = float((gl_VertexID & 1) << 2);
  float y = float((gl_VertexID & 2) << 1);
  gl_Position = vec4(x - 1.0, y - 1.0, 0, 1);
}
 
  index.frag
  #version 300 es
precision highp float;
out vec4 fragColor;
uniform vec2 uResolution;
uniform vec2 uMouse;
uniform float uTime;
const float PI = 3.1415926;
vec2 rot2(vec2 p, float angle) {
  float s = sin(angle);
  float c = cos(angle);
  return vec2(c * p.x - s * p.y, s * p.x + c * p.y);
}
vec3 rotX(vec3 p, float angle) {
  return vec3(p.x, rot2(p.yz, angle));
}
float checkerd(vec2 p) {
  return mod(floor(p.x) + floor(p.y), 2.0);
}
void main() {
  
  vec2 p = (2.0 * gl_FragCoord.xy - uResolution.xy) / min(uResolution.x, uResolution.y);
  
  
  float angle = uMouse.y / uResolution.y * -0.5 * PI;
  
  
  vec3 cameraPos = vec3(0.0, 0.0, 0.0); 
  vec3 cameraDir = vec3(0.0, 0.0, -1.0); 
  vec3 cameraUp = vec3(0.0, 1.0, 0.0); 
  
  
  cameraDir = rotX(cameraDir, angle);
  cameraUp = rotX(cameraUp, angle);
  
  
  vec3 cameraRight = cross(cameraDir, cameraUp);
  
  
  float targetDepth = 1.0;
  
  
  
  vec3 ray = cameraRight * p.x + cameraUp * p.y + cameraDir * targetDepth;
  
  ray -= cameraPos;
  
  ray = normalize(ray);
  
  
  vec3 groundNormal = vec3(0.0, 1.0, 0.0);
  
  
  float rayGroundAngle = dot(ray, groundNormal);
  
  
  vec3 outColor = vec3(0.0);
  
  
  if (rayGroundAngle < 0.0) {
    
    float groundHeight = uMouse.x / uResolution.x + 1.0;
    
    vec3 hit = cameraPos - ray * groundHeight / rayGroundAngle;
    
    float texture = checkerd(hit.zx);
    outColor = vec3(texture);
  }
  
  fragColor = vec4(outColor, 1.0);
}
 
            
    
  render.ts
  import { Space } from "@/lib/canvas/index"
import { Program } from "@/lib/webgl/program"
import { Timer } from "@/lib/control/timer"
import { Clock } from "@/lib/event/clock"
import { UniformLoader } from "@/lib/webgl/uniform-loader"
import { MouseCoords } from "@/lib/control/mouse-coords"
import vert from "./index.vert?raw"
import frag from "./index.frag?raw"
export const onload = () => {
  const space = new Space("gl-canvas")
  const canvas = space.canvas
  const gl = space.gl
  if (!canvas || !gl) return
  let timer: Timer
  let clock: Clock
  let mouse: MouseCoords
  const uniforms = new UniformLoader(gl, ["uResolution", "uTime", "uMouse"])
  const onResize = () => {
    space.fitScreen()
    render()
  }
  const configure = () => {
    space.fitScreen()
    gl.clearColor(0.0, 0.0, 0.0, 1.0)
    gl.clearDepth(1.0)
    const program = new Program(gl, vert, frag)
    clock = new Clock()
    timer = new Timer()
    mouse = new MouseCoords(canvas, 0, 0)
    uniforms.init(program)
    timer.start()
    space.onResize = onResize
  }
  const render = () => {
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
    uniforms.float("uTime", timer.elapsed * 0.001)
    uniforms.fvector2("uResolution", [canvas.width, canvas.height])
    uniforms.fvector2("uMouse", mouse.xy)
    gl.drawArrays(gl.TRIANGLE_FAN, 0, 3)
  }
  const init = () => {
    configure()
    clock.on("tick", render)
  }
  init()
}