canvas+webgl实现三维机器之心旋转动画效果代码

代码语言:html

所属分类:三维

代码描述:canvas+webgl实现三维机器之心旋转动画效果代码

代码标签: canvas webgl 三维 机器 之心 旋转 动画

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  
  
  
  
<style>
::-webkit-scrollbar {
	width: 0.625rem;
	height: 0.625rem;
}

::-webkit-scrollbar-thumb {
	background: #111;
	border-radius: 0.3125rem;
	box-shadow: inset 0.125rem 0.125rem 0.125rem rgba(255, 255, 255, 0.25),
		inset -0.125rem -0.125rem 0.125rem rgba(0, 0, 0, 0.25);
	cursor: default;
}

::-webkit-scrollbar-track {
	background: #333;
}

::selection {
	background: #fff;
	color: #333;
}

html,
body {
	height: 100vh;
	height: 100dvh;
	margin: 0;
	overflow: hidden;
}

body {
	display: grid;
	grid-template-rows: calc(100dvh - 4rem) 4rem;
	font-family: system-ui, sans-serif;
}

canvas,
.editor,
#controls {
	grid-row: 1;
	grid-column: 1;
}

canvas {
	width: 100%;
	height: auto;
	object-fit: contain;
	background: black;
	touch-action: none;
}

.editor,
.overlay,
#error {
	background: repeating-linear-gradient(0deg, #000a, #1119, #000a 0.25rem);
	padding: 1em;
}

.editor {
	color: #fefefe;
	tab-size: 2;
	border: none;
	resize: none;
}

.editor:focus {
	outline: none;
}

#error {
	grid-row: 2;
	grid-column: 1;
	margin: 0;
	padding-block: 0;
	padding-top: 0.5em;
	color: firebrick;
	overflow: auto;
	text-wrap: pretty;
}

#indicator {
	visibility: hidden;
	position: absolute;
	top: calc(var(--top, 0px) - var(--scroll-top, 0px));
	width: 0;
	height: 0;
	border-top: 10px solid transparent;
	border-bottom: 10px solid transparent;
	border-left: 10px solid firebrick;
	transform: translateY(-25%);
}

.overlay {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	margin: 0;
}

.editor,
.overlay {
	font-size: 1rem;
	line-height: 1.2;
	white-space: pre;
}

#controls {
	position: fixed;
	top: 1em;
	right: 2em;
}

.controls {
	position: relative;
	display: flex;
	gap: 1.5em;
	padding: 0.5em 1.25em;
	background: #1111;
	border-radius: 4px;
}

.controls::before,
.controls::after {
	content: "";
	position: absolute;
	z-index: -1;
	inset: 0;
	transform: scale(0.95);
	border-radius: inherit;
	opacity: 0;
}

.controls::before {
	background: #aef;
	animation: pulse 2s infinite;
}

.controls::after {
	background: #fefefe66;
	transition: transform 200ms ease-in-out;
}

.controls:hover::before,
.controls:hover::after {
	opacity: 1;
}

.controls:hover::before {
	transform: scale(0.98);
	filter: blur(2px);
}

.controls:hover::after {
	transform: scale(1.025, 1.1);
}

.controls:hover {
	background: #111f;
}

@keyframes pulse {
	0% {
		transform: scale(1);
	}

	50% {
		transform: scale(1.0125);
	}

	100% {
		transform: scale(1);
	}
}

.hidden {
	display: none !important;
}

.opaque {
	opacity: 1 !important;
	background: #111 !important;
}

input {
	all: unset;
	opacity: 0.2;
	filter: saturate(0) invert(1);
	cursor: pointer;
	transition: opacity 200ms ease-in-out;
	padding: 0.25em 0.5em;
}

input:hover {
	opacity: 1;
}

.icon {
	text-align: center;
	line-height: 1;
}

#btnToggleView {
	width: 1.25em;
}

#btnToggleView::after {
	content: "👁";
}

#btnToggleView:checked::after {
	content: "✏️";
}

#btnToggleResolution::after {
	content: "1️⃣";
}

#btnToggleResolution:checked::after {
	content: "2️⃣";
}

#btnReset::after {
	content: "⏮️";
}
</style>


  
</head>

<body translate="no">
  <canvas id="canvas"></canvas>
<textarea id="codeEditor" class="editor" spellcheck="false" autocorrect="off" autocapitalize="off" translate="no" oninput="render()"></textarea>
<pre id="error"></pre>
<div id="indicator"></div>
<div id="controls">
	<div class="controls">
		<input id="btnToggleView" class="icon" type="checkbox" name="toggleView" onclick="toggleView()">
		<input id="btnToggleResolution" class="icon" type="checkbox" name="toggleResolution" onchange="toggleResolution()">
		<input id="btnReset" class="icon" type="checkbox" name="reset" onclick="reset()">
	</div>
</div>
<script type="x-shader/x-fragment">#version 300 es
/*********
* made by Matthias Hurrle (@atzedent)
*/ 
precision highp float;
out vec4 O;
uniform float time;
uniform vec2 resolution;
uniform vec2 move;
uniform int pointerCount;
#define P pointerCount
#define FC gl_FragCoord.xy
#define R resolution
#define T time
#define N normalize
#define S smoothstep
#define rot(a) mat2(cos(a-vec4(0,11,33,0)))

float smin(float a, float b, float k) {
	float h = clamp(.5+.5*(b-a)/k,.0,1.);
	return mix(b,a,h)-k*h*(1.-h);
}

vec3 smthng(vec3 a, vec3 b, float k) {
	vec3 h=clamp(.5+.5*(b/a),.0,1.);
	return mix(b,a,h)-k*h*(1.-h);
}

vec3 kifs(vec3 p) {
	for (int i=0; i<2; i++) {
		p.yz*=rot(T);
		p.xz*=rot(T);
		p=smthng(p,-p,-1.);
		p-=vec3(.26,.02,.1);
	}
	return p;
}

float box(vec3 p, vec3 s, float r) {
	p=abs(p)-(s-r);

	return length(max(p,.0))+min(.0,max(max(p.x,p.y),p.z))-r;
}

float mspo(vec3 p, vec3 s, float r, float l) {
	const float k=.25;

	float
	d=box(p,s,r),
	res=d,
	f=1.,
	i=.0;

	for(; i++<l;) {
		vec3
		a=mod(p*f,2.)-1.,
		r=abs(1.-3.*abs(a));

		f*=3.;

		float
		da=max(r.x,r.y),
		db=max(r.y,r.z),
		dc=max(r.z,r.x),
		c=(smin(da,smin(db,dc,k),k)-.75)/f;

		if(c>d) d=c, res = c;
	}

	return res;
}

float glow=.0;
float map(vec3 p) {	
	vec3 st=p;
	st=kifs(st);
	float t=T*2., k=.025,
	sp=length(st-vec3(.5*sin(t), .125*cos(t), -.5*(sin(T)*.125+.8)))-k;
	sp=min(sp,length(st-vec3(pow(.15*-cos(t*.7),2.), .125*-sin(t), 0))-k);
	sp=min(sp,length(st-vec3(pow(.125*cos(t*1.1),2.), .125*sin(t), .5*(cos(T)*.125+.8)))-k);
	glow+=.05/(.05+sp*sp*80.);
	return min(mspo(p, vec3(1),.005,3.), sp);
}

float calcAO(in vec3 pos, in vec3 nor) {
	float occ=.0, sca=1.;
	for(float i=.0; i++<5.;) {
		float
		h=.01+.12*float(i)/4.,
		d=map(pos+h*nor);
		occ+=(h-d)*sca;
		sca*=.95;
		if(occ>.35) break;
	}
	return clamp(1.-3.*occ,.0,1.)*(.5+.5*nor.y);
}

vec3 norm(vec3 p) {
	float h=1e-3; vec2 k=vec2(-1,1);
	return N(
		k.xyy*map(p+k.xyy*h)+
		k.yxy*map(p+k.yxy*h)+
		k.yyx*map(p+k.yyx*h)+
		k.xxx*map(p+k.xxx*h)
	);
}

void cam(inout vec3 p) {
	p.yz*=rot(move.y*6.3/min(R.x,R.y)+(P>0?.0:sin(T*.5)*.5+.5));
	p.xz*=rot(-move.x*6.3/min(R.x,R.y)+(P>0?.0:T*.7));
}

void main() {
	vec2 uv=(FC-.5*R)/min(R.x,R.y);
	vec3 col=vec3(0),
	p=vec3(0,0,-(-S(-1.,-2.,log(2.*pow(sin(T*.2),2.))))-4.),
	ro=p,
	rd=N(vec3(uv,1));
	cam(p); cam(rd);
	
	float dd=.0;
	for (float i=.0; i++<400.;) {
		float d=map(p);
		if (d<1e-3 || d>12.) break;
		p+=rd*d;
		dd+=d;
	}
	
	vec3 n=norm(p),
	lp=vec3(0,1,-3);
	cam(lp);
	vec3
	l=N(lp-p);

	float
	falloff=.1+10./dot(lp-p,lp-p),
	diffuse=clamp(dot(l,n),.0,1.),
	occlusion=calcAO(p,n);
	
	col+=diffuse*occlusion*falloff;

	vec3 ambient=vec3(1,.95,.8);
	col+=.1*ambient;
		
	if (dd>12.) col=mix(vec3(0),vec3(.35,.5,.65),S(.7,.0,length(uv)));
	
	col+=vec3(.95,.4,.8)*glow*glow;
	
	O=vec4(col,1);
}</script>
  
      <script >
/*********
 * made by Matthias Hurrle (@atzedent)
 */
let editMode = false; // set to false to hide the code editor on load
let resolution = 0.5; // set 1 for full resolution or to .5 to start with half resolution on load
let renderDelay = 1000; // delay in ms before rendering the shader after a change
let dpr = Math.max(1, resolution * window.devicePixelRatio);
let frm, source, editor, store, renderer, pointers;
const shaderId = "It Is All Just a Reflection";
window.onload = init;

function resize() {
	const { innerWidth: width, innerHeight: height } = window;

	canvas.width = width * dpr;
	canvas.height = height * dpr;

	if (renderer) {
		renderer.updateScale(dpr);
	}
}
function toggleView() {
	editor.hidden = btnToggleView.checked;
}
function reset() {
	let shader = source;
	editor.text = shader ? shader.textContent : renderer.defaultSource;
	store.putShaderSource(shaderId, editor.text);
	renderThis();
}
function toggleResolution() {
	resolution = btnToggleResolution.checked ? 0.5 : 1;
	dpr = Math.max(1, resolution * window.devicePixelRatio);
	pointers.updateScale(dpr);
	resize();
}
function loop(now) {
	renderer.updateMouse(pointers.first);
	renderer.updatePointerCount(pointers.count);
	renderer.updatePointerCoords(pointers.coords);
	renderer.updateMove(pointers.move);
	renderer.render(now);
	frm = requestAnimationFrame(loop);
}
function renderThis() {
	editor.clearError();
	store.putShaderSource(shaderId, editor.text);

	const result = renderer.test(editor.text);

	if (result) {
		editor.setError(result);
	} else {
		renderer.updateShader(editor.text);
	}
	cancelAnimationFrame(frm); // Always cancel the previous frame!
	loop(0);
}
const debounce = (fn, delay) => {
	let timerId;
	return (...args) => {
		clearTimeout(timerId);
		timerId = setTimeout(() => fn.apply(this, args), delay);
	};
};
const render = debounce(renderThis, renderDelay);
function init() {
	source = document.querySelector("script[type='x-shader/x-fragment']");

	document.title = "🥚";

	renderer = new Renderer(canvas, dpr);
	pointers = new PointerHandler(canvas, dpr);
	store = new Store(window.location);
	editor = new Editor(codeEditor, error, indicator);
	editor.text = source.textContent;
	renderer.setup();
	renderer.init();

	if (!editMode) {
		btnToggleView.checked = true;
		toggleView();
	}
	if (resolution === 0.5) {
		btnToggleResolution.checked = true;
		toggleResolution();
	}
	canvas.addEventListener("shader-error", (e) => editor.setError(e.detail));

	resize();

	if (renderer.test(source.textContent) === null) {
		renderer.updateShader(source.textContent);
	}
	loop(0);
	window.onresize = resize;
	window.addEventListener("keydown", (e) => {
		if (e.key === "L" && e.ctrlKey) {
			e.preventDefault();
			btnToggleView.checked = !btnToggleView.checked;
			toggleView();
		}
	});
}
class Renderer {
	#vertexSrc = "#version 300 es\nprecision highp float;\nin vec4 position;\nvoid main(){gl_Position=position;}";
	#fragmtSrc = "#version 300 es\nprecision highp float;\nout vec4 O;\nuniform float time;\nuniform vec2 resolution;\nvoid main() {\n\tvec2 uv=gl_FragCoord.xy/resolution;\n\tO=vec4(uv,sin(time)*.5+.5,1);\n}";
	#vertices = [-1, 1, -1, -1, 1, 1, 1, -1];
	constructor(canvas, scale) {
		this.canvas = canvas;
		this.scale = scale;
		this.gl = canvas.getContext("webgl2");
		this.gl.viewport(0, 0, canvas.width * scale, canvas.height * scale);
		this.shaderSource = this.#fragmtSrc;
		this.mouseMove = [0, 0];
		this.mouseCoords = [0, 0];
		this.pointerCoords = [0, 0];
		this.nbrOfPointers = 0;
	}
	get defaultSource() {
		return this.#fragmtSrc;
	}
	updateShader(source) {
		this.reset();
		this.shaderSource = source;
		this.setup();
		this.init();
	}
	updateMove(deltas) {
		this.mouseMove = deltas;
	}
	updateMouse(coords) {
		this.mouseCoords = coords;
	}
	updatePointerCoords(coords) {
		this.pointerCoords = coords;
	}
	updatePointerCount(nbr) {
		this.nbrOfPointers = nbr;
	}
	updateScale(scale) {
		this.scale = scale;
		this.gl.viewport(0, 0, this.canvas.width * scale, this.canvas.height * scale);
	}
	compile(shader, source) {
		const gl = this.gl;
		gl.shaderSource(shader, source);
		gl.compileShader(shader);

		if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
			console.error(gl.getShaderInfoLog(shader));
			this.canvas.dispatchEvent(
				new CustomEvent("shader-error", { detail: gl.getShaderInfoLog(shader) })
			);
		}
	}
	test(source) {
		let result = null;
		const gl = this.gl;
		const shader = gl.createShader(gl.FRAGMENT_SHADER);
		gl.shaderSource(shader, source);
		gl.compileShader(shader);

		if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
			result = gl.getShaderInfoLog(sh.........完整代码请登录后点击上方下载按钮下载查看

网友评论0