HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<h4>Ray Tracing in One Weekend</h4>
参考:https://raytracing.github.io/<br>
FPS: <span id="fps"></span><br>
const vertexShaderSource = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
}
`;
const fragmentShaderSource = `
precision highp float;
uniform float u_Time;
uniform vec2 u_Resolution;
const int samples_per_pixel = 40;
const float PI = 3.14159265359;
const int MAX_DEPTH = 10;
const int MATERIAL_LAMBERTIAN = 1; // 拡散
const int MATERIAL_METAL = 2; // 金属
const int MATERIAL_DIELECTRIC = 3; // ガラス
// ------------------------------------------------------------------------------------------------ Ray
struct Ray {
vec3 orig;
vec3 dir;
};
vec3 Ray_at(Ray r, float t) {
return r.orig + t * r.dir;
}
struct Material {
int type;
vec3 albedo;
float fuzz;
float ref_idx;
};
struct HitRecord {
vec3 p;
vec3 normal;
float t;
bool front_face;
Material material;
};
void HitRecord_setFaceNormal(inout HitRecord rec, Ray r, vec3 outward_normal) {
rec.front_face = dot(r.dir, outward_normal) < 0.0;
rec.normal = rec.front_face ? outward_normal : -outward_normal;
}
// ------------------------------------------------------------------------------------------------ utils
float rndNum;
// 0.0~1.0を返す疑似乱数生成
float random() {
vec3 seed = vec3(gl_FragCoord.xy, rndNum);
rndNum = rndNum + 1.0;
float dt = dot(seed.xy, vec2(12.9898, 78.233)) + seed.z;
return fract(sin(dt) * 43758.5453);
}
// [min,max) の実数乱数を返す
float random_range(float min, float max) {
return min + (max - min) * random();
}
vec3 random_vec3() {
return vec3(random(), random(), random());
}
vec3 random_vec3_range(float min, float max) {
return vec3(random_range(min, max), random_range(min, max), random_range(min, max));
}
vec3 random_in_unit_sphere() {
for (int i = 0; i < 100; i++) {
vec3 p = random_vec3_range(-1.0, 1.0);
if (dot(p, p) < 1.0) {
return p;
}
}
return vec3(0.0); // 諦める
}
vec3 random_unit_vector() {
float a = random_range(0.0, 2.0 * PI);
float z = random_range(-1.0, 1.0);
float r = sqrt(1.0 - z * z);
return vec3(r * cos(a), r * sin(a), z);
}
vec3 random_in_unit_disk() {
for (int i = 0; i < 100; i++) {
vec3 p = vec3(random_range(-1.0, 1.0), random_range(-1.0, 1.0), 0);
if (dot(p, p) < 1.0) {
return p;
}
}
return vec3(0.0); // 諦める
}
float degrees_to_radians(float degrees) {
return degrees * PI / 180.0;
}
// ------------------------------------------------------------------------------------------------ マテリアル
Material Material_initLambertian(vec3 albedo) {
return Material(MATERIAL_LAMBERTIAN, albedo, 0.0, 0.0);
}
Material Material_initMetal(vec3 albedo, float fuzz) {
return Material(MATERIAL_METAL, albedo, fuzz, 0.0);
}
Material Material_initDielectric(float ref_idx) {
return Material(MATERIAL_DIELECTRIC, vec3(0.0), 0.0, ref_idx);
}
float schlick(float cosine, float ref_idx) {
float r0 = (1.0 - ref_idx) / (1.0 + ref_idx);
r0 = r0 * r0;
return r0 + (1.0 - r0) * pow((1.0 - cosine), 5.0);
}
bool Material_scatter(Material mat, Ray r_in, HitRecord rec, inout vec3 attenuation, inout Ray scattered) {
if (mat.type == MATERIAL_METAL) {
vec3 reflected = reflect(normalize(r_in.dir), rec.normal);
scattered = Ray(rec.p, reflected + mat.fuzz * random_in_unit_sphere());
attenuation = mat.albedo;
return (dot(scattered.dir, rec.normal) > 0.0);
} else if (mat.type == MATERIAL_LAMBERTIAN) {
vec3 scatter_direction = rec.normal + random_unit_vector();
scattered = Ray(rec.p, scatter_direction);
attenuation = mat.albedo;
return true;
} else if (mat.type == MATERIAL_DIELECTRIC) {
attenuation = vec3(1.0, 1.0, 1.0);
float etai_over_etat = (rec.front_face) ? (1.0 / mat.ref_idx) : (mat.ref_idx);
vec3 unit_direction = normalize(r_in.dir);
float cos_theta = min(dot(-unit_direction, rec.normal), 1.0);
float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
if (etai_over_etat * sin_theta > 1.0) {
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = Ray(rec.p, reflected);
return true;
}
float reflect_prob = schlick(cos_theta, etai_over_etat);
if (random() < reflect_prob) {
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = Ray(rec.p, reflected);
return true;
}
vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
scattered = Ray(rec.p, refracted);
return true;
}
}
// ------------------------------------------------------------------------------------------------ 球体
const int SPHERES_SIZE = 4;
struct Sphere {
vec3 center;
float radius;
Material material;
};
Sphere spheres[SPHERES_SIZE];
bool Sphere_hit(Sphere sphere, Ray r, float ray_tmin, float ray_tmax, inout HitRecord rec) {
vec3 oc = sphere.center - r.orig;
float a = dot(r.dir, r.dir);
float h = dot(r.dir, oc);
float c = dot(oc, oc) - sphere.radius * sphere.radius;
float discriminant = h * h - a * c;
if (discriminant < 0.0) {
return false;
}
float sqrtd = sqrt(discriminant);
// Find the nearest root that lies in the acceptable range.
float root = (h - sqrtd) / a;
if (root <= ray_tmin || ray_tmax <= root) {
root = (h + sqrtd) / a;
if (root <= ray_tmin || ray_tmax <= root) {
return false;
}
}
rec.t = root;
rec.p = Ray_at(r, rec.t);
rec.material = sphere.material;
vec3 outward_normal = (rec.p - sphere.center) / sphere.radius;
HitRecord_setFaceNormal(rec, r, outward_normal);
return true;
}
bool spheresHit(Ray r, float ray_tmin, float ray_tmax, inout HitRecord rec) {
HitRecord temp_rec;
bool hit_anything = false;
float closest_so_far = ray_tmax;
for (int i = 0; i < SPHERES_SIZE; i++) {
if (Sphere_hit(spheres[i], r, ray_tmin, closest_so_far, temp_rec)) {
hit_anything = true;
closest_so_far = temp_rec.t;
rec = temp_rec;
}
}
return hit_anything;
}
// ------------------------------------------------------------------------------------------------ camera
struct Camera {
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
vec3 u, v, w;
float lens_radius;
};
void Camera_init(inout Camera camera, vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect_ratio, float aperture, float focus_dist) {
float theta = degrees_to_radians(vfov);
float h = tan(theta / 2.0);
float viewport_height = 2.0 * h;
float viewport_width = aspect_ratio * viewport_height;
camera.w = normalize(lookfrom - lookat);
camera.u = normalize(cross(vup, camera.w));
camera.v = cross(camera.w, camera.u);
camera.origin = lookfrom;
camera.horizontal = focus_dist * viewport_width * camera.u;
camera.vertical = focus_dist * viewport_height * camera.v;
camera.lower_left_corner = camera.origin - camera.horizontal / 2.0 - camera.vertical / 2.0 - focus_dist * camera.w;
camera.lens_radius = aperture / 2.0;
}
Ray Camera_getRay(inout Camera camera, float u, float v) {
vec3 rd = camera.lens_radius * random_in_unit_disk();
vec3 offset = u * rd.xxx + v * rd.yyy;
return Ray(camera.origin + offset, camera.lower_left_corner + u * camera.horizontal + v * camera.vertical - camera.origin - offset);
}
// ------------------------------------------------------------------------------------------------ render
vec3 ray_color(Ray r) {
vec3 accumulated_color = vec3(1.0, 1.0, 1.0);
for (int depth = 0; depth < MAX_DEPTH; depth++) {
HitRecord rec;
// レイがオブジェクトにヒットしたかを確認
if (spheresHit(r, 0.001, 3.402823466e+38, rec)) {
Ray scattered;
vec3 attenuation;
if (!Material_scatter(rec.material, r, rec, attenuation, scattered)) {
return vec3(0.0);
}
r = scattered;
accumulated_color *= attenuation;
} else {
// 背景色を計算
vec3 unit_direction = normalize(r.dir);
float a = 0.5 * (unit_direction.y + 1.0);
accumulated_color *= mix(vec3(1.0, 1.0, 1.0), vec3(0.5, 0.7, 1.0), a);
return accumulated_color;
}
}
return vec3(0.0);
}
// ------------------------------------------------------------------------------------------------ main
void main() {
rndNum = 0.0;
spheres[0] = Sphere(vec3(0.0, 0.0, 0.0), 0.5, Material_initLambertian(vec3(0.7, 0.3, 0.3)));
spheres[1] = Sphere(vec3(0.0, -100.5, 0.0), 100.0, Material_initLambertian(vec3(0.8, 0.8, 0.0))); // big
float a = -u_Time / 600.0;
float dis = cos(u_Time / 1200.0) * 0.3 + 1.3;
spheres[2] = Sphere(vec3(cos(a) * dis, 0.0, sin(a) * dis), 0.5, Material_initMetal(vec3(.8, .8, .8), 0.0));
a += PI;
spheres[3] = Sphere(vec3(cos(a) * dis, 0.0, sin(a) * dis), 0.5, Material_initDielectric(1.5));
Camera cam;
vec3 lookfrom = vec3(3.0, 2.0 + sin(u_Time / 1300.0) * 2.0, 3.0);
vec3 lookat = vec3(0.0, 0.0, 0.0);
vec3 vup = vec3(0.0, 1.0, 0.0);
float dist_to_focus = length(lookfrom - lookat);
float aperture = 0.1;
Camera_init(cam, lookfrom, lookat, vup, 20.0, u_Resolution.x / u_Resolution.y, aperture, dist_to_focus);
vec3 pixel_color = vec3(0.0, 0.0, 0.0);
for (int i = 0; i < samples_per_pixel; i++) {
float u = (gl_FragCoord.x + random() - 0.5) / (u_Resolution.x - 1.0);
float v = (gl_FragCoord.y + random() - 0.5) / (u_Resolution.y - 1.0);
Ray r = Camera_getRay(cam, u, v);
pixel_color += ray_color(r);
}
float scale = 1.0 / float(samples_per_pixel);
gl_FragColor = vec4(sqrt(scale * pixel_color.r), sqrt(scale * pixel_color.g), sqrt(scale * pixel_color.b), 1.0);
//gl_FragColor = vec4(gl_FragCoord.x / u_Resolution.x, random(), random(), 1.0);
}
`;
$(async () => {
const aspect_ratio = 16.0 / 9.0;
const image_width = 400;
let image_height = Math.floor(image_width / aspect_ratio);
image_height = (image_height < 1) ? 1 : image_height;
const canvas = document.createElement("canvas") as HTMLCanvasElement;
canvas.width = image_width;
canvas.height = image_height;
document.body.appendChild(canvas);
const canvasWebGL = new CanvasWebGL(canvas, vertexShaderSource, fragmentShaderSource);
let fps = 0;
let lastSection = 0;
const onFrame = (time: DOMHighResTimeStamp) => {
const section = Math.floor(time / 1000);
if (lastSection != section) {
lastSection = section;
$("#fps").text(fps);
fps = 0;
}
fps++;
canvasWebGL.render(time);
requestAnimationFrame(onFrame);
};
requestAnimationFrame(onFrame);
});
class CanvasWebGL {
private readonly gl: WebGLRenderingContext;
private readonly uTimeLocation: WebGLUniformLocation | null;
constructor(canvas: HTMLCanvasElement, vertexShaderSource: string, fragmentShaderSource: string) {
const gl = canvas.getContext("webgl");
if (!gl) {
throw "WebGL is not supported!";
}
this.gl = gl;
const vertexShader = this.createShader(gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = this.createShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program link error:', gl.getProgramInfoLog(program));
}
gl.useProgram(program);
const vertices = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const aPosition = gl.getAttribLocation(program, "a_Position");
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
const uResolutionLocation = gl.getUniformLocation(program, "u_Resolution");
gl.uniform2f(uResolutionLocation, canvas.width, canvas.height);
this.uTimeLocation = gl.getUniformLocation(program, "u_Time");
//this.render(0);
}
render(time: number) {
const gl = this.gl;
gl.uniform1f(this.uTimeLocation, time);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
private createShader(type: GLenum, source: string): WebGLShader {
const shader = this.gl.createShader(type);
if (!shader) {
throw "shader can't created!"
}
this.gl.shaderSource(shader, source);
this.gl.compileShader(shader);
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
throw `Shader compile error: ${this.gl.getShaderInfoLog(shader)}`;
}
return shader;
}
}
Also see: Tab Triggers