diff --git a/CODEBASE_DOCUMENTATION.md b/CODEBASE_DOCUMENTATION.md index e569a32..276a971 100644 --- a/CODEBASE_DOCUMENTATION.md +++ b/CODEBASE_DOCUMENTATION.md @@ -8,6 +8,9 @@ WebGL procedural texture generator built on mebiusbox's MIT-licensed pixy.js sha ENTRY POINTS: editor.html Main editor (loads src/app.js) index.html Redirect to showcase.html + pinkthositive-dashboard.html One-page prompt-led dashboard for Pinkthositive hero constructs plus usable material studies + pinkthositive-preview.html One-page live preview for Moon Surface Hero and Squid Tentacles Hero custom GLSL renders + pinkthositive-materials.html One-page 3D material study for seam-safe moon sphere variations and tentacle mesh skin gallery.html Live animated gallery of 70+ effects showcase.html Feature showcase (pipeline + custom GLSL previews) demos.html Interactive material demos @@ -20,6 +23,7 @@ ENTRY POINTS: PROJECT TRACKING: FINDINGS.md Findings checklist and usability roadmap + PINKTHOSITIVE_SHADER_WORKFLOW.md Pinkthositive-specific guide for hero shaders, materials, baking, prompts, and engine usage CORE MODULES (src/): app.js Main coordinator — init, animate, render @@ -68,11 +72,16 @@ PRESET & LOADER: LIBRARY (DO NOT MODIFY): pixy.module.min.js 439KB MIT-licensed shader library by mebiusbox — 70+ effects, noise functions, Composer + shader-defs.js Shared full-screen custom GLSL hero shader library + pinkthositive-dashboard-app.js Runtime bootstrap for the Pinkthositive shader dashboard + pinkthositive-dashboard-shaders.js New Pinkthositive shader catalog with prompt metadata and fragment shaders + pinkthositive-usable-materials.js Geometry-bound Pinkthositive material catalog for the dashboard's usable pack + pinkthositive-material-shaders.js Dedicated 3D material shaders for moon sphere and tentacle mesh studies DATA: presets/ 38 JSON preset files (13 custom + 25 built-in) presets/manifest.json Manifest for the preset browser (labels, categories, file paths) - presets/projects/ Example multi-layer project JSONs + presets/projects/ 11 multi-layer project JSON presets (4 examples + 7 Pinkthositive additions) sprites/ 160 pre-rendered sprite sheet PNGs (6×6 grid, 192px frames) game-sprites/ Split-frame outputs for game engine imports images/grunge.png Texture used by shader effects @@ -205,7 +214,7 @@ Exportable individually or as ZIP bundle. | 6 | DONE | Undo/redo (JSON snapshots, Ctrl+Z/Y) | | 7 | DONE | Enhanced export (PNG/JPEG/ZIP, resolution selector) | | 8 | FUTURE | Node graph editor | -| — | DONE | 38 gallery presets, 13 custom presets | +| — | DONE | 38 single-effect presets + 11 project presets (including 7 Pinkthositive additions), 13 custom presets | | — | DONE | 100 procedural sprite sheets (pixy effects) | | — | DONE | 19 custom GLSL shaders (raymarched, fractals, physics) | | — | DONE | Sprite sheet gallery with category filters | @@ -230,7 +239,7 @@ xvfb-run --auto-servernum --server-args="-screen 0 1280x1024x24" node save-custo | pixy.module.min.js | 1 | 439KB | | src/ modules | 22 | ~3,800 lines | | sprites/ | 118 | ~133MB | -| presets/ | 38 | ~200KB | +| presets/ | 50 | ~232KB | | gen-custom.html (GLSL) | 1 | ~1,350 lines | ## Dependencies diff --git a/PINKTHOSITIVE_SHADER_WORKFLOW.md b/PINKTHOSITIVE_SHADER_WORKFLOW.md new file mode 100644 index 0000000..b755807 --- /dev/null +++ b/PINKTHOSITIVE_SHADER_WORKFLOW.md @@ -0,0 +1,588 @@ +# Pinkthositive Shader Workflow + +This document explains what was built for the Pinkthositive moon and squid work, what those outputs actually are, how they were made, how to make more work like this, and how to move the results into engines such as Three.js and Roblox. + +## Related Files + +- `shader-defs.js` +- `pinkthositive-dashboard.html` +- `pinkthositive-dashboard-app.js` +- `pinkthositive-dashboard-shaders.js` +- `pinkthositive-usable-materials.js` +- `pinkthositive-preview.html` +- `pinkthositive-material-shaders.js` +- `pinkthositive-materials.html` +- `presets/projects/moon-surface-hero.json` +- `presets/projects/squid-tentacles-hero.json` + +## What These Outputs Actually Are + +There are three different artifact types involved here: + +| Type | What it is | Best use | Bad use | +| --- | --- | --- | --- | +| Hero shader render | A custom GLSL render designed as a beauty shot | Landing pages, concept art, promo stills, animated showcases | Wrapping directly onto a sphere or model | +| Material shader | A shader that runs on actual mesh geometry | Surfaces, planets, props, model skins | Replacing geometry that should exist in 3D | +| Texture set | Baked 2D images such as albedo, normal, roughness, AO, emissive | Engines that cannot run your custom shader directly | Preserving interactive procedural controls | + +For this project: + +- The `hero` moon and squid pages are custom GLSL beauty renders. +- The later `materials` page is a procedural 3D scene. +- The moon material study is a real sphere material. +- The tentacle study is not just a texture. It is a combination of: + - actual tentacle geometry + - a custom tentacle skin shader + - real 3D sucker meshes attached to the tentacles + +That last page is much closer to a procedural model study than a plain texture. + +## What Was Built + +### 1. Moon Hero + +The first good moon result was a hero render: + +- cratered heightfield look +- hard directional light +- cold rim highlights +- cinematic camera angle + +Why it worked: + +- moon terrain reads mostly from large-scale forms and lighting +- a hero shot can cheat perspective and composition + +Why it is not the same thing as a reusable game material: + +- it contains a horizon and a composed view +- it is screen-space art, not sphere-space material logic + +### 2. Squid Hero + +The first good squid result was also a hero render: + +- silhouette-first tentacle shapes +- suction cup rows +- wet highlights +- magenta and cyan art direction + +Why it worked: + +- the eye reads creatures from silhouette first +- broad tentacle bodies sold the subject better than abstract texture noise + +Why it was not enough: + +- it still was a composed shot, not a reusable model material + +### 3. Moon Material Study + +The later moon page was the correct approach for putting a moon-like surface on a sphere: + +- crater logic based on sphere direction +- seam-safe object-space/sphere-space sampling +- multiple variations on actual spheres + +Why it worked: + +- the pattern lives on the sphere, not on a flat image +- there is no camera-baked horizon in the texture logic + +### 4. Tentacle Material Study + +The later tentacle page became the right answer only after moving from fake cup painting toward real form: + +- `TubeGeometry` tentacle meshes for the primary forms +- custom skin shader for wet flesh, veins, specular, and glow +- attached 3D sucker meshes for readable depth + +That is the important lesson: + +- readable subjects need primary form in geometry +- shading should support the form, not try to invent the form alone + +## The Core Process That Worked + +The useful workflow was: + +1. Define the target artifact before writing shader code. +2. Decide where the truth of the image should live. +3. Build primary forms first. +4. Add secondary surface detail second. +5. Add lighting and color last. +6. Validate against the actual subject, not just whether it looks "cool." + +### Step 1. Pick The Right Artifact + +Before touching code, answer this: + +- Is this a beauty shot? +- Is this a reusable material? +- Is this a texture set for export? +- Is this a model study with procedural shading? + +If this is wrong, the whole result drifts. + +Examples: + +- `Moon on a sphere` means material shader, not hero render. +- `Squid tentacle anatomy` means geometry plus material, not pure fullscreen texture. +- `Nebula background` means hero shader is fine. + +### Step 2. Decide Where The Truth Lives + +There are four common spaces: + +| Space | Good for | Risk | +| --- | --- | --- | +| Screen space | Hero renders, portals, VFX, concept shots | Does not wrap correctly on models | +| UV space | Authored textures, decals, baked maps | Seams and stretching | +| Object space | Props, planets, materials tied to a mesh | Repeats per object instance | +| World space | Terrain, triplanar materials, shared environments | Can swim if objects move through it | + +The moon material used sphere/object-space logic. + +The squid hero used screen space. + +The tentacle material study used actual mesh geometry plus shader shading. + +### Step 3. Block Primary Forms First + +This is where the early failed passes taught the right lesson. + +Do not start with fine noise and hope it becomes a recognizable subject. + +Start with: + +- silhouette +- mass +- large landmarks +- anatomy or terrain logic + +Examples: + +- Moon: + - basin shapes + - crater bowls + - crater rims + - broad mare regions +- Squid: + - tentacle taper + - arm clustering + - underside cup placement + - terminal shape behavior + +### Step 4. Add Secondary Detail + +Only after the big read is correct: + +- micro pitting +- regolith grain +- slime sheen +- flesh mottling +- rim sparkle +- cup lip detail + +This detail should never be doing the job of primary form. + +### Step 5. Add Lighting And Color + +Lighting is what makes the surface believable. + +Examples from this work: + +- Moon: + - hard sun direction + - soft hemisphere fill + - cold rim tint +- Squid: + - wet specular + - soft magenta flesh lighting + - cyan accent glow + - clearcoat-like highlights on suckers + +### Step 6. Validate Aggressively + +The practical loop was: + +1. render live page +2. inspect for shader compile errors +3. check for black frames +4. capture screenshots +5. compare against the intended subject +6. refine the weakest read + +The big correction in this project was not "tune the parameters more." It was "change the artifact type." + +## The Most Important Design Rule + +For recognizable subjects: + +- texture-first works for surfaces +- geometry-first works for anatomy + +That is why: + +- the moon could become a convincing material +- the squid needed actual tentacle meshes and then real sucker geometry + +## Meta Prompt Template + +The useful meta prompt is not "make me a cool shader." + +It should specify: + +- subject +- artifact type +- target engine +- whether it must wrap on geometry +- whether it must be tileable +- what must read at silhouette level +- what can be cheated in shading +- what outputs are required + +Use this structure: + +```md +Create a [hero render | material shader | seamless texture set | model study] of [subject]. + +Target use: +- [promo still | sphere material | mesh skin | sprite sheet | baked PBR maps] + +Subject read requirements: +- Must clearly read as [subject] +- Must show [3-5 primary landmarks] +- Must avoid reading as [common failure mode] + +Technical constraints: +- [screen space | UV space | object space | world space] +- [tileable / non-tileable] +- [live shader / baked maps] +- [resolution] +- [animation yes/no] + +Deliverables: +- [live GLSL page] +- [albedo / normal / roughness / AO / emissive] +- [mesh or geometry requirement] + +Art direction: +- Palette: +- Lighting: +- Motion: +- Surface qualities: +``` + +## Prompt Examples + +### Good Prompt For A Moon Material + +```md +Create a seam-safe moon material shader for an actual sphere, not a fullscreen beauty shot. + +Target use: +- reusable planet material in Three.js +- must hold up when orbiting a camera around the sphere + +Subject read requirements: +- crater bowls, raised rims, ejecta feel, dusty regolith, darker mare zones +- must read as lunar terrain, not generic rock noise +- avoid visible UV seam and avoid a baked horizon + +Technical constraints: +- object-space or sphere-space logic +- live shader first, then bake albedo, normal, roughness, and AO +- 1024 map target + +Art direction: +- grey and silver base +- subtle blue-white cold rim light +- hard directional sun +``` + +### Good Prompt For A Squid Tentacle Study + +```md +Create a tentacle model study, not just a tentacle texture. + +Target use: +- readable creature appendage for real-time rendering + +Subject read requirements: +- tentacle taper +- underside sucker placement +- wet flesh read +- clustered arm arrangement +- avoid parallel neon tubes and avoid flat painted cup illusions + +Technical constraints: +- geometry-first +- custom skin shader for flesh and sheen +- actual 3D sucker geometry +- export path should support baking for engines that cannot run GLSL + +Art direction: +- deep magenta and purple flesh +- bioluminescent cyan accents +- glossy wet highlights +``` + +## How To Produce More "Stuff" + +Use this decision grid: + +| If the subject is... | Start with... | Then add... | +| --- | --- | --- | +| Terrain, rock, bark, moon, rust | Material shader | Baked PBR maps | +| Creature appendage, anatomy, iconic object | Geometry or strong silhouette | Skin shader and secondary detail | +| Portal, nebula, energy beam, abstract VFX | Hero shader | Sprite sheet or video bake | +| Mobile or engine-limited target | Baked textures early | Reduced live procedural complexity | + +A reliable production sequence is: + +1. reference the subject +2. pick artifact type +3. build primary read +4. validate +5. only then add fancy detail +6. decide whether to keep it live or bake it + +## Export Strategy + +There are three real export strategies. + +### 1. Keep It As A Live Shader + +Use this when: + +- you are shipping on the web +- you want interactive parameters +- you want infinite variation from uniforms + +Good targets: + +- Three.js +- custom WebGL apps +- custom engines with shader support + +### 2. Bake It To Texture Maps + +Use this when: + +- the engine cannot run your custom shader +- you need consistent art across tools +- you are targeting standard PBR workflows + +Typical outputs: + +- albedo/base color +- normal +- roughness +- ambient occlusion +- metalness when relevant +- emissive when relevant +- height/displacement when relevant + +### 3. Bake It To Sprites Or Video + +Use this when: + +- it is mostly a visual effect +- the effect is camera-facing +- the target engine is limited + +Typical outputs: + +- sprite sheet +- flipbook atlas +- looping video +- alpha sequence + +## How To Use These Results In Three.js + +There are two main paths. + +### Live Shader Path + +Use `THREE.ShaderMaterial` when you want the effect to stay procedural. + +That is what the hero renders and moon/tentacle material studies are doing. + +Core pieces: + +- geometry +- `ShaderMaterial` +- uniforms +- render loop + +Good for: + +- moon material on a sphere +- animated creature skin +- live web demos + +### Baked PBR Path + +If you bake maps, use `THREE.MeshStandardMaterial` or `THREE.MeshPhysicalMaterial`. + +Typical map assignment: + +- `map` +- `normalMap` +- `roughnessMap` +- `aoMap` +- `metalnessMap` +- `emissiveMap` + +Use this path when: + +- you need engine-friendly content +- you want simpler runtime cost +- you want to reuse the look across many scenes + +### Practical Three.js Notes + +- `ShaderMaterial` is the correct path for custom GLSL materials. +- `WebGLRenderTarget` is the correct path when you want to render a shader into a texture and then reuse that texture. +- `MeshStandardMaterial` is the correct path for baked PBR maps. +- normal maps affect lighting, not actual silhouette. +- if silhouette matters, use geometry or displacement. + +## How To Use These Results In Roblox + +Roblox is the most important constraint case: + +- Roblox does not use your arbitrary GLSL directly in the same way a custom WebGL app does. +- For Roblox, the reliable path is to bake the result. + +### Best Roblox Path For Static Or Prop-Like Assets + +1. build the look procedurally +2. bake texture maps +3. import the mesh as `MeshPart` +4. use `SurfaceAppearance` with baked PBR maps + +Useful baked outputs for Roblox: + +- albedo +- normal +- roughness +- metalness when relevant + +Important practical constraints: + +- use UV-mapped meshes +- Roblox supports PBR textures on `SurfaceAppearance` +- normal maps should be OpenGL tangent-space normals +- mesh objects get one material assignment + +### Best Roblox Path For Tentacles Or Creatures + +For something like the tentacle study: + +- export the geometry from Blender or a DCC as `fbx` or `gltf` +- keep the tentacle form and sucker geometry in the mesh +- bake the shading into texture maps +- animate the mesh or rig separately + +Do not plan on shipping the browser GLSL directly into Roblox. + +### Best Roblox Path For Effects + +For shader-like effects that are mostly visual: + +- bake sprite sheets +- bake flipbooks +- bake animated image sequences + +Treat Roblox as a bake target, not as the place where the original GLSL lives. + +## How To Use These Results "Anywhere" + +The portable rule is simple: + +- if the target can run your shader language and pipeline, keep it live +- otherwise bake it + +### Unity / Unreal / Godot + +Usually you will either: + +- rebuild the shader in the engine's shader graph or shading language +- or bake the maps and use the engine's standard material system + +### Blender / DCC + +Use the shader or material study as the source lookdev pass, then: + +- bake maps +- export mesh plus textures +- move to glTF, FBX, or engine-native formats + +## What The Current Repo Can Export Today + +There is an important difference between the editor pipeline and the custom Pinkthositive pages. + +### Editor Pipeline + +The main editor already has: + +- texture export +- PBR generation +- 3D preview + +That is the standard export path for editor-built layered textures. + +### Custom Pinkthositive Pages + +The Pinkthositive hero and material pages are live custom shader pages. + +Right now they are best for: + +- live display +- look development +- visual reference +- screenshot or capture + +If these need formal content export, the next proper system is: + +1. render the custom material into fixed-resolution `WebGLRenderTarget`s +2. output dedicated passes for albedo, normal, roughness, AO, emissive, and height +3. save those as PNGs + +That would turn the current live studies into a proper asset bake pipeline. + +## Recommended Technique Library + +If we keep producing more work like this, the useful technique set is: + +- sphere-space crater fields for planets +- object-space procedural rock and bark materials +- triplanar mapping for seam hiding +- mesh-based creature appendages +- attached procedural secondary geometry +- offscreen bake passes for export +- sprite sheet baking for VFX targets + +## Honest Lessons From These Passes + +The failed versions were useful because they exposed the wrong abstraction. + +The main lessons were: + +- not every subject should start as a procedural texture preset +- a good-looking abstract surface is not the same as a readable subject +- creatures need silhouette and anatomy +- planets need correct material space +- shader quality is less about "more noise" and more about "right representation" + +## External References + +These platform notes were checked against official docs: + +- Three.js `ShaderMaterial`: https://threejs.org/docs/pages/ShaderMaterial.html +- Three.js `MeshStandardMaterial`: https://threejs.org/docs/api/en/materials/MeshStandardMaterial +- Three.js `WebGLRenderTarget`: https://threejs.org/docs/pages/WebGLRenderTarget.html +- Three.js render targets manual: https://threejs.org/manual/en/rendertargets.html +- Roblox texture specifications and `SurfaceAppearance`: https://create.roblox.com/docs/art/modeling/texture-specifications diff --git a/pinkthositive-dashboard-app.js b/pinkthositive-dashboard-app.js new file mode 100644 index 0000000..71a0969 --- /dev/null +++ b/pinkthositive-dashboard-app.js @@ -0,0 +1,259 @@ +import * as THREE from "three"; +import { PINKTHOSITIVE_DASHBOARD_SHADERS } from "./pinkthositive-dashboard-shaders.js"; +import { MATERIAL_VERT, PINKTHOSITIVE_USABLE_MATERIALS } from "./pinkthositive-usable-materials.js"; + +const FULLSCREEN_VERT = ` +varying vec2 vUv; +void main(){ + vUv=uv; + gl_Position=vec4(position.xy,0.0,1.0); +} +`; + +class FullscreenShaderCard { + constructor(card, shaderDef) { + this.card = card; + this.canvas = card.querySelector("canvas"); + this.renderer = new THREE.WebGLRenderer({ + canvas: this.canvas, + antialias: true, + preserveDrawingBuffer: true, + powerPreference: "high-performance" + }); + this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); + this.scene = new THREE.Scene(); + this.camera = new THREE.Camera(); + this.uniforms = { + time: { value: 0 }, + resolution: { value: new THREE.Vector2(1, 1) } + }; + this.material = new THREE.ShaderMaterial({ + vertexShader: FULLSCREEN_VERT, + fragmentShader: shaderDef.shader, + uniforms: this.uniforms, + depthTest: false, + depthWrite: false + }); + this.scene.add(new THREE.Mesh(new THREE.PlaneGeometry(2, 2), this.material)); + this.resize(); + } + + resize() { + const rect = this.canvas.getBoundingClientRect(); + this.renderer.setSize(rect.width, rect.height, false); + const drawSize = new THREE.Vector2(); + this.renderer.getDrawingBufferSize(drawSize); + this.uniforms.resolution.value.copy(drawSize); + } + + render(t) { + this.uniforms.time.value = t; + this.renderer.render(this.scene, this.camera); + } +} + +function makeGeometry(type) { + switch (type) { + case "box": + return new THREE.BoxGeometry(1.8, 1.8, 1.8, 24, 24, 24); + case "icosahedron": + return new THREE.IcosahedronGeometry(1.28, 4); + case "octahedron": + return new THREE.OctahedronGeometry(1.34, 2); + case "torusknot": + return new THREE.TorusKnotGeometry(0.88, 0.28, 220, 32, 2, 5); + case "sphere": + default: + return new THREE.SphereGeometry(1.28, 120, 72); + } +} + +class MaterialShaderCard { + constructor(card, materialDef) { + this.card = card; + this.canvas = card.querySelector("canvas"); + this.renderer = new THREE.WebGLRenderer({ + canvas: this.canvas, + antialias: true, + alpha: false, + preserveDrawingBuffer: true, + powerPreference: "high-performance" + }); + this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); + this.renderer.outputColorSpace = THREE.SRGBColorSpace; + this.renderer.toneMapping = THREE.ACESFilmicToneMapping; + this.renderer.toneMappingExposure = 1.24; + + this.scene = new THREE.Scene(); + this.scene.background = new THREE.Color(0x040811); + this.camera = new THREE.PerspectiveCamera(34, 1, 0.1, 100); + this.camera.position.set(0, 0.34, 4.3); + + this.group = new THREE.Group(); + this.scene.add(this.group); + + this.uniforms = { + uTime: { value: 0 }, + uLightDir: { value: new THREE.Vector3(-0.62, 0.74, 0.48) } + }; + + this.mesh = new THREE.Mesh( + makeGeometry(materialDef.geometry), + new THREE.ShaderMaterial({ + vertexShader: MATERIAL_VERT, + fragmentShader: materialDef.fragmentShader, + uniforms: this.uniforms + }) + ); + this.group.add(this.mesh); + + const base = new THREE.Mesh( + new THREE.CylinderGeometry(1.58, 1.8, 0.28, 54), + new THREE.MeshStandardMaterial({ + color: 0x0d131f, + roughness: 0.82, + metalness: 0.16 + }) + ); + base.position.set(0, -1.72, 0); + this.group.add(base); + + const ring = new THREE.Mesh( + new THREE.TorusGeometry(1.62, 0.05, 14, 80), + new THREE.MeshBasicMaterial({ + color: 0x5fa9d8, + transparent: true, + opacity: 0.38 + }) + ); + ring.rotation.x = Math.PI * 0.5; + ring.position.set(0, -1.5, 0); + this.group.add(ring); + + const hemi = new THREE.HemisphereLight(0x6f96ba, 0x020306, 0.78); + this.scene.add(hemi); + const back = new THREE.PointLight(0x2f6fa4, 0.55, 10); + back.position.set(-1.8, 1.4, -2.2); + this.scene.add(back); + + this.resize(); + } + + resize() { + const rect = this.canvas.getBoundingClientRect(); + this.renderer.setSize(rect.width, rect.height, false); + this.camera.aspect = rect.width / rect.height; + this.camera.updateProjectionMatrix(); + } + + render(t) { + this.uniforms.uTime.value = t; + this.mesh.rotation.y = t * 0.42; + this.mesh.rotation.x = Math.sin(t * 0.36) * 0.16; + this.group.rotation.y = Math.sin(t * 0.2) * 0.08; + this.renderer.render(this.scene, this.camera); + } +} + +function makePromptList(lines) { + return lines.map(line => `
  • ${line}
  • `).join(""); +} + +function buildCard(def, statusLabel, extraFactLabel) { + const article = document.createElement("article"); + article.className = "artifact-card"; + article.innerHTML = ` +
    +
    +

    ${def.category}

    +

    ${def.title}

    +
    + ${def.artifactType} +
    +
    + + ${statusLabel} +
    +
    +

    ${def.description}

    +
    +
    + Target + ${def.targetUse} +
    +
    + Export Path + ${def.exportPath} +
    +
    + ${extraFactLabel} + ${def.geometry ? def.geometry : def.artifactType} +
    +
    +
    +

    Prompt Vector

    + +
    +
    + `; + return article; +} + +function mountSection({ defs, gridId, statusLabel, factory, extraFactLabel }) { + const grid = document.getElementById(gridId); + return defs.map(def => { + const el = buildCard(def, statusLabel, extraFactLabel); + grid.appendChild(el); + return factory(el, def); + }); +} + +function renderDashboard() { + const heroCards = mountSection({ + defs: PINKTHOSITIVE_DASHBOARD_SHADERS, + gridId: "dashboard-grid", + statusLabel: "Live GLSL", + factory: (el, def) => new FullscreenShaderCard(el, def), + extraFactLabel: "Artifact" + }); + + const materialCards = mountSection({ + defs: PINKTHOSITIVE_USABLE_MATERIALS, + gridId: "usable-grid", + statusLabel: "Live Material", + factory: (el, def) => new MaterialShaderCard(el, def), + extraFactLabel: "Preview Mesh" + }); + + const counts = { + constructs: PINKTHOSITIVE_DASHBOARD_SHADERS.length + PINKTHOSITIVE_USABLE_MATERIALS.length, + live: PINKTHOSITIVE_DASHBOARD_SHADERS.length, + usable: PINKTHOSITIVE_USABLE_MATERIALS.length + }; + + document.querySelector("[data-stat='constructs']").textContent = String(counts.constructs); + document.querySelector("[data-stat='live']").textContent = String(counts.live); + document.querySelector("[data-stat='usable']").textContent = String(counts.usable); + + const allCards = [...heroCards, ...materialCards]; + const resize = () => { + for (const card of allCards) { + card.resize(); + } + }; + + window.addEventListener("resize", resize); + resize(); + + const start = performance.now(); + const frame = now => { + const t = (now - start) * 0.001; + for (const card of allCards) { + card.render(t); + } + requestAnimationFrame(frame); + }; + requestAnimationFrame(frame); +} + +renderDashboard(); diff --git a/pinkthositive-dashboard-shaders.js b/pinkthositive-dashboard-shaders.js new file mode 100644 index 0000000..b4337ce --- /dev/null +++ b/pinkthositive-dashboard-shaders.js @@ -0,0 +1,452 @@ +const COMMON_GLSL = ` +float hash(float n){return fract(sin(n)*43758.5453123);} +float hash21(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453123);} +mat2 rot2(float a){float c=cos(a),s=sin(a);return mat2(c,-s,s,c);} + +vec3 mod289(vec3 x){return x-floor(x*(1.0/289.0))*289.0;} +vec4 mod289(vec4 x){return x-floor(x*(1.0/289.0))*289.0;} +vec4 permute(vec4 x){return mod289(((x*34.0)+1.0)*x);} +vec4 taylorInvSqrt(vec4 r){return 1.79284291400159-0.85373472095314*r;} + +float snoise(vec3 v){ + const vec2 C=vec2(1.0/6.0,1.0/3.0); + const vec4 D=vec4(0.0,0.5,1.0,2.0); + vec3 i=floor(v+dot(v,C.yyy)); + vec3 x0=v-i+dot(i,C.xxx); + vec3 g=step(x0.yzx,x0.xyz); + vec3 l=1.0-g; + vec3 i1=min(g.xyz,l.zxy); + vec3 i2=max(g.xyz,l.zxy); + vec3 x1=x0-i1+C.xxx; + vec3 x2=x0-i2+C.yyy; + vec3 x3=x0-D.yyy; + i=mod289(i); + vec4 p=permute(permute(permute( + i.z+vec4(0.0,i1.z,i2.z,1.0)) + +i.y+vec4(0.0,i1.y,i2.y,1.0)) + +i.x+vec4(0.0,i1.x,i2.x,1.0)); + float n_=0.142857142857; + vec3 ns=n_*D.wyz-D.xzx; + vec4 j=p-49.0*floor(p*ns.z*ns.z); + vec4 x_=floor(j*ns.z); + vec4 y_=floor(j-7.0*x_); + vec4 x=x_*ns.x+ns.yyyy; + vec4 y=y_*ns.x+ns.yyyy; + vec4 h=1.0-abs(x)-abs(y); + vec4 b0=vec4(x.xy,y.xy); + vec4 b1=vec4(x.zw,y.zw); + vec4 s0=floor(b0)*2.0+1.0; + vec4 s1=floor(b1)*2.0+1.0; + vec4 sh=-step(h,vec4(0.0)); + vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy; + vec4 a1=b1.xzyw+s1.xzyw*sh.zzww; + vec3 p0=vec3(a0.xy,h.x); + vec3 p1=vec3(a0.zw,h.y); + vec3 p2=vec3(a1.xy,h.z); + vec3 p3=vec3(a1.zw,h.w); + vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3))); + p0*=norm.x;p1*=norm.y;p2*=norm.z;p3*=norm.w; + vec4 m=max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0); + m=m*m; + return 42.0*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3))); +} + +float fbm(vec3 p){ + float v=0.0; + float a=0.5; + for(int i=0;i<6;i++){ + v+=a*snoise(p); + p*=2.03; + a*=0.5; + } + return v; +} + +float sdBox(vec3 p,vec3 b){ + vec3 q=abs(p)-b; + return length(max(q,0.0))+min(max(q.x,max(q.y,q.z)),0.0); +} + +float sdOcta(vec3 p,float s){ + p=abs(p); + return (p.x+p.y+p.z-s)*0.57735027; +} + +float opUnion(float a,float b){return min(a,b);} +`; + +const PRISM_CATHEDRAL = ` +uniform float time; +uniform vec2 resolution; +${COMMON_GLSL} + +vec2 opU(vec2 a,vec2 b){return a.x44.0){ + matId=d.y; + break; + } + t+=d.x*0.7; + } + return t; +} + +vec3 materialColor(float matId,vec3 p,vec3 n,vec3 rd){ + vec3 keyDir=normalize(vec3(-0.45,0.72,-0.52)); + float diff=max(dot(n,keyDir),0.0); + float rim=pow(1.0-max(dot(n,-rd),0.0),3.0); + float spec=pow(max(dot(reflect(-keyDir,n),-rd),0.0),24.0); + vec3 col=vec3(0.0); + + if(matId<1.5){ + float lane=0.5+0.5*sin(p.z*5.0-time*3.6); + float seam=smoothstep(0.95,0.2,abs(fract(p.z*0.24)-0.5)); + col=mix(vec3(0.03,0.05,0.08),vec3(0.05,0.11,0.16),lane); + col+=vec3(0.05,0.34,0.62)*pow(seam,5.0)*0.35; + col+=vec3(0.65,0.74,0.82)*spec*0.16; + }else if(matId<2.5){ + col=mix(vec3(0.22,0.25,0.31),vec3(0.75,0.8,0.86),diff*0.7+0.2); + col+=vec3(0.25,0.75,1.0)*pow(max(0.0,1.0-abs(p.z)*1.5),4.0)*0.16; + }else if(matId<3.5){ + col=mix(vec3(0.22,0.17,0.09),vec3(0.94,0.79,0.43),diff*0.8+0.2); + col+=vec3(1.0,0.86,0.52)*spec*0.25; + }else if(matId<4.5){ + float pulse=0.5+0.5*sin(time*2.4+p.z*1.8); + col=mix(vec3(0.08,0.18,0.28),vec3(0.62,0.93,1.0),pulse); + col+=vec3(0.75,0.96,1.0)*spec*0.35; + }else{ + col=mix(vec3(0.16,0.14,0.18),vec3(0.72,0.46,0.24),diff*0.6+0.2); + } + + col*=0.22+diff*0.95; + col+=vec3(0.08,0.24,0.42)*rim*0.5; + return col; +} + +void main(){ + vec2 uv=(gl_FragCoord.xy/resolution.xy)*2.0-1.0; + uv.x*=resolution.x/resolution.y; + vec3 ro=vec3(0.0,0.25,-6.0); + ro.x+=sin(time*0.23)*0.45; + ro.y+=sin(time*0.11)*0.12; + vec3 ta=vec3(0.0,0.1,2.0); + vec3 ww=normalize(ta-ro); + vec3 uu=normalize(cross(vec3(0.0,1.0,0.0),ww)); + vec3 vv=normalize(cross(ww,uu)); + vec3 rd=normalize(uu*uv.x+vv*uv.y+ww*1.65); + + float matId; + vec3 hit=ro; + float t=trace(ro,rd,matId,hit); + vec3 col=vec3(0.005,0.008,0.018); + + if(t<44.0){ + vec3 n=calcNormal(hit); + col=materialColor(matId,hit,n,rd); + float fog=exp(-t*0.055); + col=mix(vec3(0.01,0.02,0.05),col,fog); + } + + float scan=pow(max(0.0,1.0-abs(uv.y+0.08)*1.4),3.0); + col+=vec3(0.08,0.28,0.52)*scan*0.06; + col=pow(max(col,0.0),vec3(0.92)); + gl_FragColor=vec4(col,1.0); +} +`; + +const STELLAR_ORCHID = ` +uniform float time; +uniform vec2 resolution; +${COMMON_GLSL} + +float starfield(vec2 uv,float scale){ + vec2 gv=uv*scale; + vec2 id=floor(gv); + vec2 f=fract(gv)-0.5; + float h=hash21(id); + float star=smoothstep(0.995,1.0,h); + return star*exp(-dot(f,f)*26.0); +} + +void main(){ + vec2 uv=gl_FragCoord.xy/resolution.xy; + vec2 p=(uv-0.5)*2.0; + p.x*=resolution.x/resolution.y; + + vec2 q=p; + q*=1.0+0.05*sin(time*0.15); + float r=length(q); + + vec3 col=vec3(0.02,0.02,0.055); + float neb=fbm(vec3(q*1.2,time*0.08))*0.5+0.5; + col+=mix(vec3(0.05,0.07,0.16),vec3(0.12,0.04,0.11),neb)*(1.0-smoothstep(0.2,1.6,r)); + col+=vec3(0.65,0.78,1.0)*starfield(p+vec2(time*0.01,0.0),60.0)*0.35; + col+=vec3(1.0,0.86,0.75)*starfield(p*1.3-vec2(0.0,time*0.02),90.0)*0.2; + + for(int i=0;i<5;i++){ + float fi=float(i); + float layer=fi/4.0; + vec2 lp=q*(1.0+layer*0.24); + lp*=rot2(time*0.07*(0.4+layer)+fi*0.7); + float lr=length(lp); + float la=atan(lp.y,lp.x); + float warp=fbm(vec3(vec2(cos(la),sin(la))*2.5,layer*2.0+time*0.12)); + float petals=pow(0.5+0.5*cos(la*(7.0+fi)+warp*1.8-time*(0.4+layer)),2.2); + float petalRadius=0.2+layer*0.16+petals*(0.22+layer*0.05); + float edge=smoothstep(petalRadius+0.07,petalRadius-0.03,lr); + float hollow=smoothstep(0.12+layer*0.035,0.25+layer*0.06,lr); + edge*=hollow; + vec3 petalColor=mix( + mix(vec3(0.24,0.08,0.16),vec3(0.88,0.24,0.44),layer), + mix(vec3(0.22,0.68,0.95),vec3(1.0,0.82,0.44),petals), + 0.42 + ); + col+=petalColor*edge*(0.26+layer*0.1); + col+=vec3(0.18,0.88,1.0)*pow(edge,2.4)*(0.035+0.03*layer); + } + + float throat=exp(-r*r*26.0); + float throatNoise=fbm(vec3(q*8.0,time*0.5))*0.5+0.5; + col+=mix(vec3(0.74,0.18,0.28),vec3(1.0,0.86,0.4),throatNoise)*throat*0.42; + float core=exp(-r*r*170.0); + col+=vec3(1.0,0.78,0.46)*core*0.18; + + float filaments=0.0; + for(int i=0;i<10;i++){ + float fi=float(i); + float ang=fi*0.62831853+sin(time*0.35+fi)*0.08; + vec2 dir=vec2(cos(ang),sin(ang)); + vec2 cp=q-dir*0.18; + float stem=exp(-pow(length(vec2(dot(cp,vec2(-dir.y,dir.x))*4.0,dot(cp,dir)-0.18)),2.0)*5.0); + filaments=max(filaments,stem); + } + col+=vec3(0.98,0.78,0.55)*filaments*0.18; + + float halo=exp(-pow(r*1.35,1.55))*0.35; + col+=vec3(0.14,0.36,0.86)*halo; + col=pow(max(col,0.0),vec3(0.92)); + gl_FragColor=vec4(col,1.0); +} +`; + +const OBSIDIAN_MONSOON = ` +uniform float time; +uniform vec2 resolution; +${COMMON_GLSL} + +vec2 opU(vec2 a,vec2 b){return a.x55.0){ + matId=d.y; + break; + } + t+=d.x*0.75; + } + return t; +} + +float rain(vec2 uv,float seed,float speed){ + vec2 gv=uv; + gv.x+=sin(gv.y*5.0+seed)*0.08; + gv.y+=time*speed; + vec2 id=floor(gv); + vec2 f=fract(gv)-0.5; + float h=hash21(id+seed); + float lane=smoothstep(0.82,1.0,h); + float drop=exp(-f.x*f.x*55.0)*smoothstep(0.52,-0.46,f.y); + return lane*drop; +} + +void main(){ + vec2 uv=(gl_FragCoord.xy/resolution.xy)*2.0-1.0; + uv.x*=resolution.x/resolution.y; + vec3 ro=vec3(0.0,-0.2,-6.8); + ro.x+=sin(time*0.18)*0.8; + vec3 ta=vec3(0.0,-0.65,4.0); + vec3 ww=normalize(ta-ro); + vec3 uu=normalize(cross(vec3(0.0,1.0,0.0),ww)); + vec3 vv=normalize(cross(ww,uu)); + vec3 rd=normalize(uu*uv.x+vv*(uv.y*0.86)+ww*1.7); + + float matId; + vec3 hit=ro; + float t=trace(ro,rd,matId,hit); + + vec3 sky=mix(vec3(0.02,0.03,0.06),vec3(0.05,0.14,0.24),smoothstep(-0.35,0.95,uv.y)); + float aurora=fbm(vec3(uv.x*1.5,uv.y*2.5,time*0.08)); + sky+=mix(vec3(0.05,0.28,0.48),vec3(0.92,0.22,0.46),smoothstep(0.2,0.9,aurora))*pow(max(0.0,1.0-abs(uv.y-0.24)*2.4),3.0)*0.52; + sky+=vec3(0.22,0.09,0.12)*pow(max(0.0,1.0-abs(uv.y+0.42)*3.6),4.0)*0.9; + float lightning=pow(max(0.0,sin(time*0.8)+0.55*sin(time*1.7+1.2)),16.0); + sky+=vec3(0.72,0.86,1.0)*lightning*0.35; + vec3 col=sky; + + if(t<55.0){ + vec3 n=calcNormal(hit); + vec3 keyDir=normalize(vec3(-0.62,0.72,-0.2)); + float diff=max(dot(n,keyDir),0.0); + float fres=pow(1.0-max(dot(n,-rd),0.0),4.0); + float spec=pow(max(dot(reflect(-keyDir,n),-rd),0.0),38.0); + + if(matId<1.5){ + float wet=0.5+0.5*fbm(vec3(hit.x*0.5,hit.z*0.5,time*0.08)); + col=mix(vec3(0.04,0.04,0.07),vec3(0.1,0.12,0.17),wet); + col+=vec3(0.12,0.58,0.92)*spec*0.75; + col+=sky*fres*0.38; + }else if(matId<2.5){ + col=mix(vec3(0.06,0.06,0.08),vec3(0.24,0.24,0.28),diff*0.7+0.12); + col+=mix(vec3(0.1,0.54,0.9),vec3(1.0,0.46,0.25),fres)*fres*0.9; + col+=vec3(0.9,0.97,1.0)*spec*0.95; + }else{ + col=mix(vec3(0.16,0.09,0.1),vec3(0.98,0.36,0.32),diff*0.8+0.22); + col+=vec3(1.0,0.76,0.52)*spec*0.4; + } + + float fog=exp(-t*0.04); + col=mix(sky,col,fog); + } + + float storm=0.0; + storm+=rain(uv*vec2(7.0,5.0),1.3,1.7); + storm+=rain(uv*vec2(10.0,7.0)+vec2(2.0,0.0),4.7,2.3)*0.7; + storm+=rain(uv*vec2(13.0,8.0)-vec2(1.5,0.0),8.1,2.8)*0.45; + col=mix(col,col+vec3(0.28,0.62,0.96),storm*0.76); + + float vignette=smoothstep(1.65,0.28,length(uv)); + col*=vignette; + col=pow(max(col,0.0),vec3(0.95)); + gl_FragColor=vec4(col,1.0); +} +`; + +export const PINKTHOSITIVE_DASHBOARD_SHADERS = [ + { + id: "prism_cathedral", + title: "Prism Cathedral", + category: "Cosmic Architecture", + artifactType: "Hero Render", + targetUse: "Title sequence / splash art / cinematic backdrop", + exportPath: "Still, looping video, or reconstructed engine corridor", + description: + "A silver-and-cyan cathedral corridor with crystal spines, hard architectural rhythm, and glowing rails moving through the frame.", + metaPrompt: [ + "Subject: impossible cathedral made of crystal and metal", + "Artifact: hero render, not a reusable tiling texture", + "Primary read: corridor silhouette, repeating arches, central crystal spine", + "Avoid: generic sci-fi noise with no structure" + ], + shader: PRISM_CATHEDRAL + }, + { + id: "stellar_orchid", + title: "Stellar Orchid", + category: "Cosmic Bioform", + artifactType: "Hero Render", + targetUse: "Poster art / animated key visual / splash screen", + exportPath: "Still, video loop, or bloom-heavy background plate", + description: + "A luminous flower-form suspended in nebula space, built from layered petals, glowing throat detail, and orbital pollen filaments.", + metaPrompt: [ + "Subject: cosmic flower that reads from silhouette first", + "Artifact: hero render for key art", + "Primary read: petal crown, glowing center, starfield depth", + "Avoid: flat kaleidoscope pattern" + ], + shader: STELLAR_ORCHID + }, + { + id: "obsidian_monsoon", + title: "Obsidian Monsoon", + category: "Storm Landscape", + artifactType: "Hero Render", + targetUse: "Environment concept / loading screen / trailer plate", + exportPath: "Still, video loop, or rebuilt scene with monolith meshes", + description: + "A field of reflective black slabs under chromatic storm light, with rain streaks, aurora spill, and wet ground glare.", + metaPrompt: [ + "Subject: monolith landscape under neon weather", + "Artifact: hero render with readable environment composition", + "Primary read: vertical slab forms, wet ground, storm atmosphere", + "Avoid: abstract rain wallpaper" + ], + shader: OBSIDIAN_MONSOON + } +]; diff --git a/pinkthositive-dashboard.html b/pinkthositive-dashboard.html new file mode 100644 index 0000000..3260120 --- /dev/null +++ b/pinkthositive-dashboard.html @@ -0,0 +1,472 @@ + + + + + + Pinkthositive Shader Dashboard + + + + +
    +
    +

    Prompt-Led Shader Lab

    +

    Pinkthositive Groundbreaking Constructs

    +

    + This dashboard turns the workflow meta-prompt into new live artifacts instead of vague style requests. Each + piece has a clear subject, artifact type, primary read target, and export path. This pass now mixes hero + renders with geometry-bound material studies so there is a meaningful difference between spectacle and + actually usable surface work. +

    + +
    +
    + New Constructs + 0 +
    +
    + Live Hero Renders + 0 +
    +
    + Usable Materials + 0 +
    +
    + Production Strategy + Prompt → Form → Light +
    +
    +
    + +
    +
    +
    +

    New Live Artifacts

    +

    + Three fresh pieces built directly from the workflow: one architectural hero render, one cosmic bioform, + and one storm environment. Each card exposes the prompt vector so the generation logic is inspectable. +

    +
    + Live GLSL +
    +
    +
    + +
    +
    +
    +

    Usable Material Pack

    +

    + Six new material studies rendered on actual geometry. These are the more production-relevant outputs: + surfaces that could be baked into maps, rebuilt in engine shaders, or used directly as look-dev sources + for props, creatures, crystals, and environment assets. +

    +
    + Material Studies +
    +
    +
    + +
    +
    +
    +

    Pinkthositive Archive

    +

    + Previous moon and squid work stays linked here so the dashboard acts as a single entry point for the + whole Pinkthositive thread: original hero experiments, material studies, and the process guide. +

    +
    + Reference +
    + +
    +
    + + + + + diff --git a/pinkthositive-material-shaders.js b/pinkthositive-material-shaders.js new file mode 100644 index 0000000..29c369b --- /dev/null +++ b/pinkthositive-material-shaders.js @@ -0,0 +1,274 @@ +const COMMON_GLSL = ` +float hash(float n){return fract(sin(n)*43758.5453123);} +float hash21(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453123);} +vec3 randDir(float seed){ + float z=hash(seed*1.17)*2.0-1.0; + float a=hash(seed*2.31)*6.28318530718; + float r=sqrt(max(0.0,1.0-z*z)); + return vec3(r*cos(a),z,r*sin(a)); +} +vec3 mod289(vec3 x){return x-floor(x*(1.0/289.0))*289.0;} +vec4 mod289(vec4 x){return x-floor(x*(1.0/289.0))*289.0;} +vec4 permute(vec4 x){return mod289(((x*34.0)+1.0)*x);} +vec4 taylorInvSqrt(vec4 r){return 1.79284291400159-0.85373472095314*r;} +float snoise(vec3 v){ + const vec2 C=vec2(1.0/6.0,1.0/3.0); + const vec4 D=vec4(0.0,0.5,1.0,2.0); + vec3 i=floor(v+dot(v,C.yyy)); + vec3 x0=v-i+dot(i,C.xxx); + vec3 g=step(x0.yzx,x0.xyz); + vec3 l=1.0-g; + vec3 i1=min(g.xyz,l.zxy); + vec3 i2=max(g.xyz,l.zxy); + vec3 x1=x0-i1+C.xxx; + vec3 x2=x0-i2+C.yyy; + vec3 x3=x0-D.yyy; + i=mod289(i); + vec4 p=permute(permute(permute( + i.z+vec4(0.0,i1.z,i2.z,1.0)) + +i.y+vec4(0.0,i1.y,i2.y,1.0)) + +i.x+vec4(0.0,i1.x,i2.x,1.0)); + float n_=0.142857142857; + vec3 ns=n_*D.wyz-D.xzx; + vec4 j=p-49.0*floor(p*ns.z*ns.z); + vec4 x_=floor(j*ns.z); + vec4 y_=floor(j-7.0*x_); + vec4 x=x_*ns.x+ns.yyyy; + vec4 y=y_*ns.x+ns.yyyy; + vec4 h=1.0-abs(x)-abs(y); + vec4 b0=vec4(x.xy,y.xy); + vec4 b1=vec4(x.zw,y.zw); + vec4 s0=floor(b0)*2.0+1.0; + vec4 s1=floor(b1)*2.0+1.0; + vec4 sh=-step(h,vec4(0.0)); + vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy; + vec4 a1=b1.xzyw+s1.xzyw*sh.zzww; + vec3 p0=vec3(a0.xy,h.x); + vec3 p1=vec3(a0.zw,h.y); + vec3 p2=vec3(a1.xy,h.z); + vec3 p3=vec3(a1.zw,h.w); + vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3))); + p0*=norm.x;p1*=norm.y;p2*=norm.z;p3*=norm.w; + vec4 m=max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0); + m=m*m; + return 42.0*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3))); +} +float fbm(vec3 p){ + float v=0.0; + float a=0.5; + for(int i=0;i<6;i++){ + v+=a*snoise(p); + p*=2.03; + a*=0.5; + } + return v; +} +`; + +const MOON_COMMON = ` +float craterFn(vec3 n,vec3 center,float radius,float depth){ + float d=1.0-dot(n,center); + float x=d/max(radius*radius,0.00008); + float r=sqrt(max(x,0.0)); + float interior=1.0-smoothstep(0.0,1.0,x); + float bowl=-pow(interior,1.45)*depth; + float rim=exp(-pow((r-1.0)/0.2,2.0))*depth*0.78; + float ejecta=exp(-x*1.7)*depth*0.08; + return bowl+rim+ejecta; +} + +float mareMask(vec3 n){ + float m=0.0; + for(int i=0;i<4;i++){ + float fi=float(i); + vec3 center=randDir(uSeed*9.7+fi*7.31); + float mareBasin=smoothstep(0.82,0.93,dot(n,center)); + m=max(m,mareBasin); + } + return m*uMare; +} + +float moonHeight(vec3 n){ + vec3 p=n*3.5+uSeed*0.3; + float h=fbm(p)*0.035; + h+=fbm(n*11.0+uSeed*0.9)*0.01; + h-=abs(snoise(n*7.5+uSeed*1.3))*0.008; + h-=mareMask(n)*0.018; + float cr=0.0; + for(int i=0;i<16;i++){ + float fi=float(i); + vec3 center=randDir(uSeed*13.0+fi*11.17); + float radius=mix(0.05,0.13,hash(uSeed*31.0+fi*2.3)); + float depth=mix(0.015,0.05,hash(uSeed*41.0+fi*5.1)); + cr+=craterFn(n,center,radius,depth); + } + for(int i=0;i<34;i++){ + float fi=float(i); + vec3 center=randDir(uSeed*71.0+fi*3.87); + float radius=mix(0.012,0.05,hash(uSeed*83.0+fi*1.7)); + float depth=mix(0.003,0.013,hash(uSeed*97.0+fi*2.9)); + cr+=craterFn(n,center,radius,depth); + } + return h+cr*uCraterAmp; +} + +vec3 moonShadeNormal(vec3 dir,float h0){ + vec3 t=normalize(abs(dir.y)<0.98?cross(dir,vec3(0.0,1.0,0.0)):cross(dir,vec3(1.0,0.0,0.0))); + vec3 b=normalize(cross(dir,t)); + float e=0.006; + float ht=moonHeight(normalize(dir+t*e)); + float hb=moonHeight(normalize(dir+b*e)); + return normalize(dir-(ht-h0)/e*t*1.4-(hb-h0)/e*b*1.4); +} +`; + +export const MOON_MATERIAL_VERT = ` +uniform float uSeed; +uniform float uMare; +uniform float uCold; +uniform float uCraterAmp; +uniform float uHeightScale; +varying vec3 vObjDir; +varying vec3 vWorldPos; +varying float vHeight; +${COMMON_GLSL} +${MOON_COMMON} + +void main(){ + vec3 dir=normalize(position); + float h=moonHeight(dir); + vec3 displaced=dir*(1.0+h*uHeightScale); + vObjDir=normalize(displaced); + vHeight=h; + vec4 world=modelMatrix*vec4(displaced,1.0); + vWorldPos=world.xyz; + gl_Position=projectionMatrix*viewMatrix*world; +} +`; + +export const MOON_MATERIAL_FRAG = ` +uniform vec3 uLightDir; +uniform float uSeed; +uniform float uMare; +uniform float uCold; +uniform float uCraterAmp; +uniform float uHeightScale; +varying vec3 vObjDir; +varying vec3 vWorldPos; +varying float vHeight; +${COMMON_GLSL} +${MOON_COMMON} + +void main(){ + vec3 dir=normalize(vObjDir); + vec3 n=moonShadeNormal(dir,vHeight); + vec3 lightDir=normalize(uLightDir); + vec3 viewDir=normalize(cameraPosition-vWorldPos); + float diff=max(dot(n,lightDir),0.0); + float hemi=0.5+0.5*n.y; + float spec=pow(max(dot(reflect(-lightDir,n),viewDir),0.0),24.0); + float rim=pow(1.0-max(dot(n,viewDir),0.0),3.0); + float mare=mareMask(dir); + float dust=fbm(dir*30.0+uSeed*2.7)*0.5+0.5; + float tone=clamp(vHeight*8.0+0.55,0.0,1.0); + vec3 highland=mix(vec3(0.18,0.18,0.19),vec3(0.63,0.63,0.66),hemi*0.65+tone*0.35); + vec3 basalt=mix(vec3(0.12,0.13,0.15),vec3(0.34,0.35,0.38),tone*0.6+0.2); + vec3 albedo=mix(highland,basalt,mare*0.88); + albedo*=mix(0.93,1.08,dust); + vec3 col=albedo*(0.18+diff*0.95); + col+=vec3(1.0,0.98,0.95)*spec*0.18; + col+=mix(vec3(0.08,0.08,0.1),vec3(0.48,0.7,1.0),uCold)*rim*0.12; + col*=1.0-clamp(-vHeight*12.0,0.0,1.0)*0.12; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.94)),1.0); +} +`; + +export const TENTACLE_MATERIAL_VERT = ` +uniform float uTime; +uniform float uSeed; +uniform float uSway; +uniform float uRadius; +varying vec2 vUv; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +${COMMON_GLSL} + +void main(){ + vUv=uv; + vec3 pos=position; + float along=uv.x; + float thickness=mix(1.1,0.18,pow(along,1.18)); + float rootBulge=exp(-pow(along/0.18,2.0))*uRadius*0.38; + pos-=normal*uRadius*(1.0-thickness); + pos+=normal*rootBulge; + float wave=sin(uTime*1.2+along*8.0+uSeed*4.3)*0.04*pow(along,1.4)*uSway; + float wave2=cos(uTime*0.9+along*11.0+uSeed*6.1)*0.028*pow(along,1.25)*uSway; + pos.x+=wave; + pos.z+=wave2; + pos+=normal*sin(along*24.0+uTime*5.0+uSeed*9.0)*0.009*(0.25+along*0.75); + vec4 world=modelMatrix*vec4(pos,1.0); + vWorldPos=world.xyz; + vWorldNormal=normalize(normalMatrix*normal); + gl_Position=projectionMatrix*viewMatrix*world; +} +`; + +export const TENTACLE_MATERIAL_FRAG = ` +uniform float uTime; +uniform float uSeed; +uniform vec3 uLightDir; +varying vec2 vUv; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +${COMMON_GLSL} + +float rowBand(float center,float width,float coord){ + float d=abs(fract(coord-center+0.5)-0.5); + return smoothstep(width,0.0,d); +} + +void main(){ + vec3 n=normalize(vWorldNormal); + vec3 lightDir=normalize(uLightDir); + vec3 viewDir=normalize(cameraPosition-vWorldPos); + + float underside=pow(max(cos((fract(vUv.y+0.22)-0.5)*6.28318530718),0.0),2.2); + float underside2=pow(max(cos((fract(vUv.y+0.36)-0.5)*6.28318530718),0.0),3.2)*0.45; + float band=max(underside,underside2); + + vec2 cupUv1=vec2(fract(vUv.x*11.0+uSeed*0.13)-0.5,(fract(vUv.y+0.22)-0.5)*3.8); + vec2 cupUv2=vec2(fract(vUv.x*11.0+0.5+uSeed*0.19)-0.5,(fract(vUv.y+0.36)-0.5)*4.4); + float cupR1=length(cupUv1); + float cupR2=length(cupUv2); + float cupRing1=exp(-pow((cupR1-0.22)/0.09,2.0))*underside; + float cupCore1=exp(-pow(cupR1/0.12,2.0))*underside; + float cupRing2=exp(-pow((cupR2-0.18)/0.08,2.0))*underside2; + float cupCore2=exp(-pow(cupR2/0.09,2.0))*underside2; + float cups=max(cupRing1,cupRing2); + float cupCore=max(cupCore1,cupCore2); + + float veins=fbm(vec3(vUv.x*14.0,vUv.y*8.0,uSeed*2.0+uTime*0.08))*0.5+0.5; + float slime=fbm(vec3(vUv.x*40.0,vUv.y*22.0,uSeed*3.0-uTime*0.12))*0.5+0.5; + float bio=band*(0.5+0.5*sin(uTime*1.6+vUv.x*18.0+uSeed*7.0))*0.18; + + vec3 baseA=vec3(0.07,0.02,0.1); + vec3 baseB=vec3(0.32,0.04,0.22); + vec3 baseC=vec3(0.78,0.15,0.45); + vec3 body=mix(baseA,baseB,pow(vUv.x,0.7)); + body=mix(body,baseC,band*0.35); + body+=vec3(0.05,0.0,0.04)*(veins-0.5)*1.1; + body*=mix(0.85,1.12,slime); + + vec3 cupCol=mix(vec3(0.6,0.28,0.44),vec3(0.93,0.68,0.8),cupCore); + body=mix(body,cupCol,clamp(cups*0.18+cupCore*0.14,0.0,1.0)); + + float diff=max(dot(n,lightDir),0.0); + float spec=pow(max(dot(reflect(-lightDir,n),viewDir),0.0),26.0); + float rim=pow(1.0-max(dot(n,viewDir),0.0),2.0); + vec3 col=body*(0.16+diff*0.94); + col+=vec3(0.98,0.98,1.0)*spec*0.34; + col+=vec3(0.1,0.95,1.0)*(bio+rim*0.08); + col+=vec3(0.05,0.18,0.22)*band*0.1; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.95)),1.0); +} +`; diff --git a/pinkthositive-materials.html b/pinkthositive-materials.html new file mode 100644 index 0000000..e826d60 --- /dev/null +++ b/pinkthositive-materials.html @@ -0,0 +1,775 @@ + + + + + + Pinkthositive Material Variations + + + + +
    +
    +

    Object-Space Material Study

    +

    Pinkthositive Surface Variations

    +

    + This page uses actual 3D materials instead of camera-baked hero renders. The moon study is seam-safe on + spheres because the crater field lives in object space. The squid study applies a tentacle skin shader to + real `TubeGeometry` tentacle meshes, with suction cups and wet sheen on the model instead of on a flat image. +

    + +
    + +
    +
    +
    +
    +

    Moon Material Variations

    +

    Three cratered moon variants on actual spheres: dense highlands, mare-heavy basalt, and a colder silver pass. Orbit the stage to inspect the seam-free surface behavior.

    +
    + Sphere Material +
    +
    + +
    +
    +
    +
    +

    Highland Craterfield

    +

    Dense crater amplitude, minimal mare flattening, brighter dust and sharper crater rims.

    +
    +
    +

    Mare Basin

    +

    Broader dark plains mixed into the crater field so it reads more like a planetary surface than a generic rock ball.

    +
    +
    +

    Cold Silver

    +

    Same seam-safe crater workflow, but with more blue-white rim light and a cleaner, colder regolith response.

    +
    +
    +
    +
    + +
    +
    +
    +

    Squid Tentacle Model Study

    +

    Procedural squid skin on tentacle meshes built from curves and `TubeGeometry`. The cups live on the underside band of the actual mesh rather than in a flat 2D composition.

    +
    + Tentacle Mesh +
    +
    + +
    +
    +
    +
    +

    Model Strategy

    +

    Five tapered tentacle meshes with different curve silhouettes, sway timing, and a shared collar so the study reads like a real creature rig instead of parallel tubes.

    +
    +
    +

    Skin Strategy

    +

    Shader now handles the wet flesh, veins, and cyan edge bloom, while real attached cup meshes provide the readable sucker depth instead of a flat painted illusion.

    +
    +
    + +
    +
    +
    +
    + + + + + diff --git a/pinkthositive-preview.html b/pinkthositive-preview.html new file mode 100644 index 0000000..169f78a --- /dev/null +++ b/pinkthositive-preview.html @@ -0,0 +1,331 @@ + + + + + + Pinkthositive Hero Renders + + + + +
    +
    +

    Custom GLSL Hero Renders

    +

    Pinkthositive Moon and Squid

    +

    + Two fully custom fragment shaders, rendered directly on this page. The moon is a cratered + heightfield hero shot instead of a noise texture. The squid render is shape-first with + broad tentacle bodies, suction cup rows, wet specular sheen, and cyan glow accents. +

    + +
    + +
    +
    +
    +

    Moon Surface Hero

    + Cosmic +
    +
    + + Live GLSL +
    +
    +

    Oblique lunar terrain render with crater basins, micro impact pitting, hard sun shadow, and cold rim glints.

    + +
    +
    + +
    +
    +

    Squid Tentacles Hero

    + Bio +
    +
    + + Live GLSL +
    +
    +

    Animated squid tentacle silhouettes with suction cups, fleshy magenta interiors, wet highlights, and bioluminescent cyan detail.

    + +
    +
    +
    +
    + + + + + diff --git a/pinkthositive-usable-materials.js b/pinkthositive-usable-materials.js new file mode 100644 index 0000000..830a128 --- /dev/null +++ b/pinkthositive-usable-materials.js @@ -0,0 +1,379 @@ +const COMMON_GLSL = ` +float hash(float n){return fract(sin(n)*43758.5453123);} +float hash21(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453123);} +mat2 rot2(float a){float c=cos(a),s=sin(a);return mat2(c,-s,s,c);} + +vec3 mod289(vec3 x){return x-floor(x*(1.0/289.0))*289.0;} +vec4 mod289(vec4 x){return x-floor(x*(1.0/289.0))*289.0;} +vec4 permute(vec4 x){return mod289(((x*34.0)+1.0)*x);} +vec4 taylorInvSqrt(vec4 r){return 1.79284291400159-0.85373472095314*r;} + +float snoise(vec3 v){ + const vec2 C=vec2(1.0/6.0,1.0/3.0); + const vec4 D=vec4(0.0,0.5,1.0,2.0); + vec3 i=floor(v+dot(v,C.yyy)); + vec3 x0=v-i+dot(i,C.xxx); + vec3 g=step(x0.yzx,x0.xyz); + vec3 l=1.0-g; + vec3 i1=min(g.xyz,l.zxy); + vec3 i2=max(g.xyz,l.zxy); + vec3 x1=x0-i1+C.xxx; + vec3 x2=x0-i2+C.yyy; + vec3 x3=x0-D.yyy; + i=mod289(i); + vec4 p=permute(permute(permute( + i.z+vec4(0.0,i1.z,i2.z,1.0)) + +i.y+vec4(0.0,i1.y,i2.y,1.0)) + +i.x+vec4(0.0,i1.x,i2.x,1.0)); + float n_=0.142857142857; + vec3 ns=n_*D.wyz-D.xzx; + vec4 j=p-49.0*floor(p*ns.z*ns.z); + vec4 x_=floor(j*ns.z); + vec4 y_=floor(j-7.0*x_); + vec4 x=x_*ns.x+ns.yyyy; + vec4 y=y_*ns.x+ns.yyyy; + vec4 h=1.0-abs(x)-abs(y); + vec4 b0=vec4(x.xy,y.xy); + vec4 b1=vec4(x.zw,y.zw); + vec4 s0=floor(b0)*2.0+1.0; + vec4 s1=floor(b1)*2.0+1.0; + vec4 sh=-step(h,vec4(0.0)); + vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy; + vec4 a1=b1.xzyw+s1.xzyw*sh.zzww; + vec3 p0=vec3(a0.xy,h.x); + vec3 p1=vec3(a0.zw,h.y); + vec3 p2=vec3(a1.xy,h.z); + vec3 p3=vec3(a1.zw,h.w); + vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3))); + p0*=norm.x;p1*=norm.y;p2*=norm.z;p3*=norm.w; + vec4 m=max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0); + m=m*m; + return 42.0*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3))); +} + +float fbm(vec3 p){ + float v=0.0; + float a=0.5; + for(int i=0;i<6;i++){ + v+=a*snoise(p); + p*=2.03; + a*=0.5; + } + return v; +} + +float triplanarNoise(vec3 p, vec3 n, float scale){ + vec3 an=pow(abs(n),vec3(4.0)); + an/=max(an.x+an.y+an.z,0.0001); + float nx=fbm(vec3(p.yz*scale,0.0)); + float ny=fbm(vec3(p.xz*scale,1.37)); + float nz=fbm(vec3(p.xy*scale,2.71)); + return nx*an.x+ny*an.y+nz*an.z; +} +`; + +export const MATERIAL_VERT = ` +varying vec3 vObjPos; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +varying vec2 vUv; + +void main(){ + vUv=uv; + vObjPos=position; + vec4 world=modelMatrix*vec4(position,1.0); + vWorldPos=world.xyz; + vWorldNormal=normalize(normalMatrix*normal); + gl_Position=projectionMatrix*viewMatrix*world; +} +`; + +const ABYSS_PEARL = ` +uniform float uTime; +uniform vec3 uLightDir; +varying vec3 vObjPos; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +varying vec2 vUv; +${COMMON_GLSL} + +void main(){ + vec3 n=normalize(vWorldNormal); + vec3 v=normalize(cameraPosition-vWorldPos); + vec3 p=normalize(vObjPos); + float swirl=fbm(p*4.0+uTime*0.05)*0.5+0.5; + float band=0.5+0.5*sin((p.y+swirl*0.4)*28.0+snoise(p*6.0)*3.0); + float shell=triplanarNoise(vObjPos*0.8,n,3.0)*0.5+0.5; + float fres=pow(1.0-max(dot(n,v),0.0),3.4); + vec3 base=mix(vec3(0.12,0.18,0.22),vec3(0.82,0.84,0.88),shell*0.7+0.15); + vec3 iridescence=mix(vec3(0.22,0.68,0.92),vec3(1.0,0.54,0.76),band); + base=mix(base,iridescence,0.24+fres*0.42); + float diff=max(dot(n,normalize(uLightDir)),0.0); + float spec=pow(max(dot(reflect(-normalize(uLightDir),n),v),0.0),52.0); + vec3 col=base*(0.22+diff*0.88); + col+=vec3(1.0,0.98,0.96)*spec*0.95; + col+=vec3(0.22,0.86,1.0)*fres*0.18; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.94)),1.0); +} +`; + +const BASILISK_SCALE = ` +uniform float uTime; +uniform vec3 uLightDir; +varying vec3 vObjPos; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +varying vec2 vUv; +${COMMON_GLSL} + +void main(){ + vec3 n=normalize(vWorldNormal); + vec3 v=normalize(cameraPosition-vWorldPos); + vec3 p=normalize(vObjPos); + vec2 suv=vec2(atan(p.z,p.x)/6.28318530718+0.5,p.y*0.5+0.5); + vec2 gv=suv*vec2(18.0,10.0); + vec2 id=floor(gv); + vec2 f=fract(gv)-0.5; + f.x+=mod(id.y,2.0)*0.5-0.25; + float scaleMask=smoothstep(0.52,0.18,length(vec2(f.x*1.2,f.y+0.16))); + float ridge=smoothstep(0.42,0.05,length(vec2(f.x*1.08,f.y+0.08))); + float veins=fbm(vec3(gv*0.6,uTime*0.05))*0.5+0.5; + vec3 base=mix(vec3(0.05,0.08,0.05),vec3(0.12,0.26,0.09),scaleMask); + base=mix(base,vec3(0.52,0.62,0.14),ridge*0.28); + base+=vec3(0.08,0.05,0.0)*(veins-0.5)*0.8; + float diff=max(dot(n,normalize(uLightDir)),0.0); + float spec=pow(max(dot(reflect(-normalize(uLightDir),n),v),0.0),24.0); + float fres=pow(1.0-max(dot(n,v),0.0),2.4); + vec3 col=base*(0.18+diff*0.9); + col+=mix(vec3(0.12,0.48,0.2),vec3(0.8,0.86,0.2),fres)*fres*0.16; + col+=vec3(1.0,0.95,0.7)*spec*0.34; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.96)),1.0); +} +`; + +const MAGMA_VEIN = ` +uniform float uTime; +uniform vec3 uLightDir; +varying vec3 vObjPos; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +varying vec2 vUv; +${COMMON_GLSL} + +void main(){ + vec3 n=normalize(vWorldNormal); + vec3 v=normalize(cameraPosition-vWorldPos); + vec3 p=vObjPos*1.3; + float stone=triplanarNoise(p,n,2.1)*0.5+0.5; + float cracks=abs(snoise(p*3.8+vec3(0.0,uTime*0.04,0.0))); + cracks=smoothstep(0.74,0.9,cracks+stone*0.16); + float ember=fbm(p*9.0+uTime*0.4)*0.5+0.5; + vec3 rock=mix(vec3(0.04,0.04,0.05),vec3(0.2,0.2,0.22),stone); + vec3 lava=mix(vec3(0.88,0.14,0.02),vec3(1.0,0.78,0.16),ember); + float diff=max(dot(n,normalize(uLightDir)),0.0); + float spec=pow(max(dot(reflect(-normalize(uLightDir),n),v),0.0),18.0); + vec3 col=mix(rock,lava,cracks); + col*=0.16+diff*0.92; + col+=lava*cracks*1.15; + col+=vec3(1.0,0.72,0.28)*spec*(0.06+cracks*0.18); + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.94)),1.0); +} +`; + +const CATHEDRAL_ALLOY = ` +uniform float uTime; +uniform vec3 uLightDir; +varying vec3 vObjPos; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +varying vec2 vUv; +${COMMON_GLSL} + +void main(){ + vec3 n=normalize(vWorldNormal); + vec3 v=normalize(cameraPosition-vWorldPos); + vec3 p=vObjPos; + vec3 ap=abs(p); + float panel=max( + smoothstep(0.47,0.49,abs(fract(p.x*1.25)-0.5)), + smoothstep(0.47,0.49,abs(fract(p.y*1.25)-0.5)) + ); + float engrave=0.5+0.5*sin((p.x+p.y)*10.0+snoise(p*4.0)*2.0); + float goldMask=smoothstep(0.7,0.86,engrave)*(1.0-panel); + vec3 metal=mix(vec3(0.16,0.18,0.22),vec3(0.7,0.74,0.8),pow(max(dot(n,vec3(0.0,1.0,0.0)),0.0),0.5)*0.25+0.2); + metal+=vec3(0.08,0.22,0.36)*pow(max(1.0-ap.z*1.3,0.0),3.0)*0.3; + vec3 gold=mix(vec3(0.34,0.24,0.08),vec3(0.98,0.78,0.34),goldMask); + vec3 base=mix(metal,gold,goldMask*0.72); + float diff=max(dot(n,normalize(uLightDir)),0.0); + float spec=pow(max(dot(reflect(-normalize(uLightDir),n),v),0.0),42.0); + float fres=pow(1.0-max(dot(n,v),0.0),4.0); + vec3 col=base*(0.2+diff*0.92); + col+=vec3(1.0,0.98,0.94)*spec*0.7; + col+=vec3(0.12,0.46,0.92)*fres*0.18; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.95)),1.0); +} +`; + +const STORMGLASS = ` +uniform float uTime; +uniform vec3 uLightDir; +varying vec3 vObjPos; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +varying vec2 vUv; +${COMMON_GLSL} + +void main(){ + vec3 n=normalize(vWorldNormal); + vec3 v=normalize(cameraPosition-vWorldPos); + vec3 p=vObjPos; + float facets=pow(abs(dot(normalize(p),n)),10.0); + float field=fbm(p*2.8+vec3(0.0,uTime*0.24,0.0))*0.5+0.5; + float arc=smoothstep(0.55,0.9,abs(snoise(p*7.0+vec3(0.0,uTime*1.2,0.0)))); + float fres=pow(1.0-max(dot(n,v),0.0),3.0); + vec3 glass=mix(vec3(0.04,0.09,0.18),vec3(0.36,0.78,0.98),field); + glass+=vec3(0.7,0.9,1.0)*facets*0.4; + float diff=max(dot(n,normalize(uLightDir)),0.0); + float spec=pow(max(dot(reflect(-normalize(uLightDir),n),v),0.0),56.0); + vec3 col=glass*(0.12+diff*0.7); + col+=vec3(0.4,0.95,1.0)*arc*(0.25+fres*0.3); + col+=vec3(1.0,0.98,0.96)*spec*0.95; + col+=vec3(0.2,0.82,1.0)*fres*0.28; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.92)),1.0); +} +`; + +const VOID_CORAL = ` +uniform float uTime; +uniform vec3 uLightDir; +varying vec3 vObjPos; +varying vec3 vWorldPos; +varying vec3 vWorldNormal; +varying vec2 vUv; +${COMMON_GLSL} + +void main(){ + vec3 n=normalize(vWorldNormal); + vec3 v=normalize(cameraPosition-vWorldPos); + vec3 p=vObjPos*1.5; + float body=triplanarNoise(p,n,2.6)*0.5+0.5; + float pores=abs(snoise(p*5.4)); + float cavity=smoothstep(0.72,0.93,pores+body*0.1); + float glow=fbm(p*7.0+uTime*0.18)*0.5+0.5; + vec3 base=mix(vec3(0.12,0.03,0.08),vec3(0.58,0.14,0.32),body); + base=mix(base,vec3(0.98,0.42,0.7),cavity*0.3); + float diff=max(dot(n,normalize(uLightDir)),0.0); + float spec=pow(max(dot(reflect(-normalize(uLightDir),n),v),0.0),20.0); + float fres=pow(1.0-max(dot(n,v),0.0),2.8); + vec3 col=base*(0.18+diff*0.85); + col+=vec3(0.3,1.0,0.92)*cavity*glow*0.55; + col+=vec3(1.0,0.84,0.9)*spec*0.25; + col+=vec3(0.28,0.96,0.86)*fres*0.12; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.94)),1.0); +} +`; + +export const PINKTHOSITIVE_USABLE_MATERIALS = [ + { + id: "abyss_pearl", + title: "Abyss Pearl", + category: "Organic Mineral", + artifactType: "Material Study", + targetUse: "Boss relic, underwater shrine prop, premium orb surface", + exportPath: "Bake albedo + roughness + normal from live preview", + geometry: "sphere", + description: "Wet nacre with layered interference bands and cold rim iridescence.", + metaPrompt: [ + "Subject: premium nacre material with pearl depth", + "Artifact: reusable material on real geometry", + "Primary read: shell layering, wet sheen, opalescent rim", + "Avoid: plain chrome ball" + ], + fragmentShader: ABYSS_PEARL + }, + { + id: "basilisk_scale", + title: "Basilisk Scale", + category: "Creature Armor", + artifactType: "Material Study", + targetUse: "Reptile hide, creature boss armor, fantasy shield skin", + exportPath: "Bake color/roughness/normal to creature UV set", + geometry: "sphere", + description: "Readable scale rows with green-gold highlights and hard reptilian specular.", + metaPrompt: [ + "Subject: creature armor surface, not abstract green noise", + "Artifact: reusable skin material", + "Primary read: scale rows, hard ridges, reptile sheen", + "Avoid: slime blob" + ], + fragmentShader: BASILISK_SCALE + }, + { + id: "magma_vein_basalt", + title: "Magma Vein Basalt", + category: "Volcanic Surface", + artifactType: "Material Study", + targetUse: "Lava cave rocks, weapon core, volcanic shrine floor", + exportPath: "Bake emissive + albedo + roughness for game materials", + geometry: "icosahedron", + description: "Dense basalt with bright lava fissures that read as actual internal heat, not random orange noise.", + metaPrompt: [ + "Subject: volcanic stone with internal heat", + "Artifact: reusable emissive rock material", + "Primary read: dark basalt mass, hot fissures, ember variation", + "Avoid: generic lava wallpaper" + ], + fragmentShader: MAGMA_VEIN + }, + { + id: "cathedral_alloy", + title: "Cathedral Alloy", + category: "Ceremonial Hard-Surface", + artifactType: "Material Study", + targetUse: "Sci-fi temple trim, premium door panels, relic machinery", + exportPath: "Bake panel masks, albedo, metalness, and roughness", + geometry: "box", + description: "Ornate brushed alloy with inset gold channels and cool specular edges.", + metaPrompt: [ + "Subject: ceremonial metal panel, not flat sci-fi grey", + "Artifact: reusable hard-surface material", + "Primary read: panel structure, gold inlays, cool metal specular", + "Avoid: bland brushed metal" + ], + fragmentShader: CATHEDRAL_ALLOY + }, + { + id: "stormglass_core", + title: "Stormglass Core", + category: "Crystal Energy", + artifactType: "Material Study", + targetUse: "Power crystal, pickup, reactor shard, magical node", + exportPath: "Bake albedo/emissive/roughness or rebuild in engine shader graph", + geometry: "octahedron", + description: "Electric cyan crystal with faceted edges, internal charge patterns, and glassy highlights.", + metaPrompt: [ + "Subject: energized crystal material on faceted geometry", + "Artifact: reusable crystal material", + "Primary read: facets, internal energy, cold glass rim", + "Avoid: plain blue glass" + ], + fragmentShader: STORMGLASS + }, + { + id: "void_coral", + title: "Void Coral", + category: "Alien Organic", + artifactType: "Material Study", + targetUse: "Alien reef, bio-architecture, creature nest growth", + exportPath: "Bake color/emissive/roughness for environment assets", + geometry: "torusknot", + description: "Porous magenta coral tissue with glowing cavities and fleshy depth.", + metaPrompt: [ + "Subject: alien coral that reads from pores and body mass", + "Artifact: reusable organic environment material", + "Primary read: cavities, soft flesh tone, inner glow", + "Avoid: pink marble" + ], + fragmentShader: VOID_CORAL + } +]; diff --git a/presets/manifest.json b/presets/manifest.json index 3d03d4f..e159137 100644 --- a/presets/manifest.json +++ b/presets/manifest.json @@ -431,5 +431,102 @@ "energy", "showcase" ] + }, + { + "id": "moon-surface", + "name": "Moon Surface", + "file": "presets/projects/moon-surface.json", + "kind": "project", + "category": "cosmic", + "description": "Cratered lunar regolith with rocky shadow pockets, dust speckle breakup, and subtle blue-white rim glow.", + "tags": [ + "cosmic", + "terrain", + "pinkthositive" + ] + }, + { + "id": "squid-tentacles", + "name": "Squid Tentacles", + "file": "presets/projects/squid-tentacles.json", + "kind": "project", + "category": "bio", + "description": "Organic tentacle flesh with suction cup structures, magenta-violet depth, and cyan bioluminescent wet sheen.", + "tags": [ + "bio", + "creature", + "pinkthositive" + ] + }, + { + "id": "pimple-surface", + "name": "Pimple Surface", + "file": "presets/projects/pimple-surface.json", + "kind": "project", + "category": "bio", + "description": "Bumpy inflamed skin texture with raised pustule forms, fleshy pink-red gradients, and glossy irritation highlights.", + "tags": [ + "bio", + "organic", + "pinkthositive" + ] + }, + { + "id": "moon-surface-v2", + "name": "Moon Surface V2", + "file": "presets/projects/moon-surface-v2.json", + "kind": "project", + "category": "cosmic", + "description": "Sharper lunar terrain study with crater basin rings, fine impact pitting, and restrained cold rim highlights.", + "tags": [ + "cosmic", + "terrain", + "pinkthositive", + "v2" + ] + }, + { + "id": "squid-tentacles-v2", + "name": "Squid Tentacles V2", + "file": "presets/projects/squid-tentacles-v2.json", + "kind": "project", + "category": "bio", + "description": "Shape-first squid tentacles with broad organic bodies, suction cup read, and cyan wet bioluminescent accents.", + "tags": [ + "bio", + "creature", + "pinkthositive", + "v2" + ] + }, + { + "id": "moon-surface-hero", + "name": "Moon Surface Hero", + "file": "presets/projects/moon-surface-hero.json", + "kind": "project", + "category": "cosmic", + "description": "Custom GLSL hero render of cratered lunar terrain with basin rims, impact pitting, and cold highland glints.", + "tags": [ + "cosmic", + "terrain", + "pinkthositive", + "hero", + "glsl" + ] + }, + { + "id": "squid-tentacles-hero", + "name": "Squid Tentacles Hero", + "file": "presets/projects/squid-tentacles-hero.json", + "kind": "project", + "category": "bio", + "description": "Custom GLSL hero render of animated squid tentacles with broad bodies, suction cups, wet sheen, and cyan glow.", + "tags": [ + "bio", + "creature", + "pinkthositive", + "hero", + "glsl" + ] } -] \ No newline at end of file +] diff --git a/presets/projects/moon-surface-hero.json b/presets/projects/moon-surface-hero.json new file mode 100644 index 0000000..e13b918 --- /dev/null +++ b/presets/projects/moon-surface-hero.json @@ -0,0 +1,35 @@ +{ + "version": 1, + "resolution": 1024, + "layers": [ + { + "name": "Moon Surface Hero", + "effectController": { + "type": "GLSL:Moon Surface Hero", + "animate": true, + "time": 0, + "resolution": "1024", + "polarConversion": false, + "tiling": false, + "normalMap": false + }, + "opacity": 1, + "blendMode": "Normal", + "visible": true + } + ], + "gradient": { + "enabled": false, + "intensity": 1, + "stops": [ + { + "position": 0, + "color": "#000000" + }, + { + "position": 1, + "color": "#ffffff" + } + ] + } +} diff --git a/presets/projects/moon-surface-v2.json b/presets/projects/moon-surface-v2.json new file mode 100644 index 0000000..dee30a8 --- /dev/null +++ b/presets/projects/moon-surface-v2.json @@ -0,0 +1,221 @@ +{ + "version": 1, + "resolution": 512, + "layers": [ + { + "name": "Lunar Highlands", + "effectController": { + "type": "CoherentNoise", + "animate": true, + "time": 14.2, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cHeightScale": 2, + "cRadialMask": 1, + "cNoiseOctave": 6, + "cNoiseFrequency": 13, + "cNoiseAmplitude": 1.1, + "cNoiseLacunarity": 3, + "cNoisePersistence": 0.38, + "cGradientNoise": 0.18, + "cValueNoise": 0.12, + "cVoronoiNoise": 0.7, + "cVoronoiCell": 0.3, + "cSimplexNoise": 0.28, + "cRepeat": 1, + "cTurbulence": 0.36, + "cRidge": 0.58, + "cRidgeOffset": 0.24, + "cScaleShift": 0.16, + "cPowerExponent": 2.4, + "cBias": 0.02, + "cGain": -0.12, + "cThreshold": 0.08, + "cInvert": 0, + "cNoiseSphereEnable": false, + "cNoiseGraphEnable": false, + "cColorBalanceShadowsR": -0.42, + "cColorBalanceShadowsG": -0.4, + "cColorBalanceShadowsB": -0.35, + "cColorBalanceMidtonesR": 0.02, + "cColorBalanceMidtonesG": 0.04, + "cColorBalanceMidtonesB": 0.08, + "cColorBalanceHighlightsR": 0.38, + "cColorBalanceHighlightsG": 0.45, + "cColorBalanceHighlightsB": 0.58, + "cToonEnable": false, + "cToonDark": 0.8, + "cToonLight": 0.95 + }, + "opacity": 1, + "blendMode": "Normal", + "visible": true + }, + { + "name": "Primary Crater Basins", + "effectController": { + "type": "Bubbles", + "animate": true, + "time": 2.7, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.34, + "cWidth": 0.42, + "cThickness": 0.92, + "cColor": 0.28, + "cBubblesVariation": 2, + "cColorBalanceShadowsR": -0.75, + "cColorBalanceShadowsG": -0.72, + "cColorBalanceShadowsB": -0.6, + "cColorBalanceMidtonesR": -0.2, + "cColorBalanceMidtonesG": -0.18, + "cColorBalanceMidtonesB": -0.08, + "cColorBalanceHighlightsR": 0.25, + "cColorBalanceHighlightsG": 0.3, + "cColorBalanceHighlightsB": 0.38 + }, + "opacity": 0.36, + "blendMode": "Multiply", + "visible": true + }, + { + "name": "Sunlit Crater Rims", + "effectController": { + "type": "Bubbles", + "animate": true, + "time": 2.1, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.34, + "cWidth": 0.14, + "cThickness": 0.95, + "cColor": 0.94, + "cBubblesVariation": 2, + "cColorBalanceShadowsR": -0.25, + "cColorBalanceShadowsG": -0.22, + "cColorBalanceShadowsB": -0.15, + "cColorBalanceMidtonesR": 0.25, + "cColorBalanceMidtonesG": 0.28, + "cColorBalanceMidtonesB": 0.34, + "cColorBalanceHighlightsR": 0.85, + "cColorBalanceHighlightsG": 0.9, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.34, + "blendMode": "SoftLight", + "visible": true + }, + { + "name": "Micro Impact Pits", + "effectController": { + "type": "CellNoise", + "animate": true, + "time": 7.5, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cNoiseFrequency": 18.5, + "cNoiseSphereEnable": false, + "cNoiseGraphEnable": false, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.98, + "cColorBalanceShadowsB": -0.78, + "cColorBalanceMidtonesR": -0.45, + "cColorBalanceMidtonesG": -0.42, + "cColorBalanceMidtonesB": -0.24, + "cColorBalanceHighlightsR": 0.05, + "cColorBalanceHighlightsG": 0.08, + "cColorBalanceHighlightsB": 0.16 + }, + "opacity": 0.18, + "blendMode": "Multiply", + "visible": true + }, + { + "name": "Regolith Grain", + "effectController": { + "type": "Speckle", + "animate": true, + "time": 3.4, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.42, + "cScale": 0.18, + "cDensity": 0.95, + "cColorBalanceShadowsR": -0.16, + "cColorBalanceShadowsG": -0.14, + "cColorBalanceShadowsB": -0.1, + "cColorBalanceMidtonesR": 0.06, + "cColorBalanceMidtonesG": 0.07, + "cColorBalanceMidtonesB": 0.1, + "cColorBalanceHighlightsR": 0.3, + "cColorBalanceHighlightsG": 0.34, + "cColorBalanceHighlightsB": 0.42 + }, + "opacity": 0.16, + "blendMode": "Overlay", + "visible": true + }, + { + "name": "Cold Ridge Glint", + "effectController": { + "type": "WaterTurbulence", + "animate": true, + "time": 0.9, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cScale": 0.32, + "cIntensity": 0.22, + "cColorBalanceShadowsR": -0.6, + "cColorBalanceShadowsG": -0.45, + "cColorBalanceShadowsB": -0.1, + "cColorBalanceMidtonesR": -0.15, + "cColorBalanceMidtonesG": 0.12, + "cColorBalanceMidtonesB": 0.42, + "cColorBalanceHighlightsR": 0.32, + "cColorBalanceHighlightsG": 0.7, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.12, + "blendMode": "SoftLight", + "visible": true + } + ], + "gradient": { + "enabled": true, + "intensity": 0.68, + "stops": [ + { + "position": 0, + "color": "#090a0d" + }, + { + "position": 0.32, + "color": "#383c43" + }, + { + "position": 0.62, + "color": "#7a8088" + }, + { + "position": 0.86, + "color": "#bec6d0" + }, + { + "position": 1, + "color": "#edf3ff" + } + ] + } +} diff --git a/presets/projects/moon-surface.json b/presets/projects/moon-surface.json new file mode 100644 index 0000000..b511991 --- /dev/null +++ b/presets/projects/moon-surface.json @@ -0,0 +1,164 @@ +{ + "version": 1, + "resolution": 512, + "layers": [ + { + "name": "Lunar Bedrock", + "effectController": { + "type": "CoherentNoise", + "animate": true, + "time": 10.8, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cHeightScale": 2, + "cRadialMask": 1, + "cNoiseOctave": 6, + "cNoiseFrequency": 9.4, + "cNoiseAmplitude": 1.35, + "cNoiseLacunarity": 3, + "cNoisePersistence": 0.44, + "cGradientNoise": 0.1, + "cValueNoise": 0.15, + "cVoronoiNoise": 0.85, + "cVoronoiCell": 0.38, + "cSimplexNoise": 0.2, + "cRepeat": 1, + "cTurbulence": 0.48, + "cRidge": 0.72, + "cRidgeOffset": 0.32, + "cScaleShift": 0.24, + "cPowerExponent": 2.8, + "cBias": 0.06, + "cGain": -0.2, + "cThreshold": 0.14, + "cInvert": 0, + "cNoiseSphereEnable": false, + "cNoiseGraphEnable": false, + "cColorBalanceShadowsR": -0.7, + "cColorBalanceShadowsG": -0.62, + "cColorBalanceShadowsB": -0.38, + "cColorBalanceMidtonesR": -0.05, + "cColorBalanceMidtonesG": 0.08, + "cColorBalanceMidtonesB": 0.25, + "cColorBalanceHighlightsR": 0.62, + "cColorBalanceHighlightsG": 0.78, + "cColorBalanceHighlightsB": 1, + "cToonEnable": false, + "cToonDark": 0.8, + "cToonLight": 0.95 + }, + "opacity": 1, + "blendMode": "Normal", + "visible": true + }, + { + "name": "Crater Shadows", + "effectController": { + "type": "CellNoise", + "animate": true, + "time": 4.6, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cNoiseFrequency": 10.8, + "cNoiseSphereEnable": false, + "cNoiseGraphEnable": false, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.95, + "cColorBalanceShadowsB": -0.7, + "cColorBalanceMidtonesR": -0.55, + "cColorBalanceMidtonesG": -0.45, + "cColorBalanceMidtonesB": -0.2, + "cColorBalanceHighlightsR": 0.1, + "cColorBalanceHighlightsG": 0.2, + "cColorBalanceHighlightsB": 0.35 + }, + "opacity": 0.42, + "blendMode": "Multiply", + "visible": true + }, + { + "name": "Regolith Dust", + "effectController": { + "type": "Speckle", + "animate": true, + "time": 1.9, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.5, + "cScale": 0.68, + "cDensity": 0.82, + "cColorBalanceShadowsR": -0.35, + "cColorBalanceShadowsG": -0.3, + "cColorBalanceShadowsB": -0.2, + "cColorBalanceMidtonesR": 0.12, + "cColorBalanceMidtonesG": 0.16, + "cColorBalanceMidtonesB": 0.24, + "cColorBalanceHighlightsR": 0.75, + "cColorBalanceHighlightsG": 0.82, + "cColorBalanceHighlightsB": 0.95 + }, + "opacity": 0.28, + "blendMode": "SoftLight", + "visible": true + }, + { + "name": "Icy Rim Glow", + "effectController": { + "type": "Caustics", + "animate": true, + "time": 0.7, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cScale": 2.1, + "cSpeed": 0.12, + "cColor": 0.45, + "cColorBalanceShadowsR": -0.9, + "cColorBalanceShadowsG": -0.7, + "cColorBalanceShadowsB": 0.15, + "cColorBalanceMidtonesR": -0.35, + "cColorBalanceMidtonesG": 0.25, + "cColorBalanceMidtonesB": 0.85, + "cColorBalanceHighlightsR": 0.45, + "cColorBalanceHighlightsG": 0.95, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.24, + "blendMode": "Screen", + "visible": true + } + ], + "gradient": { + "enabled": true, + "intensity": 0.72, + "stops": [ + { + "position": 0, + "color": "#07090e" + }, + { + "position": 0.35, + "color": "#3a3f4a" + }, + { + "position": 0.68, + "color": "#8f97a3" + }, + { + "position": 0.9, + "color": "#c8d3e3" + }, + { + "position": 1, + "color": "#f6f8ff" + } + ] + } +} diff --git a/presets/projects/pimple-surface.json b/presets/projects/pimple-surface.json new file mode 100644 index 0000000..90e03ae --- /dev/null +++ b/presets/projects/pimple-surface.json @@ -0,0 +1,147 @@ +{ + "version": 1, + "resolution": 512, + "layers": [ + { + "name": "Dermis Base", + "effectController": { + "type": "FbmNoise2", + "animate": true, + "time": 6.9, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cNoiseOctave": 6, + "cNoiseFrequency": 3.4, + "cNoiseAmplitude": 0.88, + "cNoiseLacunarity": 2.7, + "cNoisePersistence": 0.5, + "cScale": 0.62, + "cNoiseSphereEnable": false, + "cNoiseGraphEnable": false, + "cColorBalanceShadowsR": 0.12, + "cColorBalanceShadowsG": -0.78, + "cColorBalanceShadowsB": -0.62, + "cColorBalanceMidtonesR": 0.92, + "cColorBalanceMidtonesG": 0.18, + "cColorBalanceMidtonesB": 0.12, + "cColorBalanceHighlightsR": 1, + "cColorBalanceHighlightsG": 0.62, + "cColorBalanceHighlightsB": 0.45 + }, + "opacity": 1, + "blendMode": "Normal", + "visible": true + }, + { + "name": "Raised Pimples", + "effectController": { + "type": "Bubbles", + "animate": true, + "time": 3.7, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.2, + "cWidth": 0.46, + "cThickness": 0.92, + "cColor": 0.42, + "cBubblesVariation": 1, + "cColorBalanceShadowsR": 0.1, + "cColorBalanceShadowsG": -0.95, + "cColorBalanceShadowsB": -0.82, + "cColorBalanceMidtonesR": 0.9, + "cColorBalanceMidtonesG": -0.15, + "cColorBalanceMidtonesB": -0.22, + "cColorBalanceHighlightsR": 1, + "cColorBalanceHighlightsG": 0.55, + "cColorBalanceHighlightsB": 0.4 + }, + "opacity": 0.62, + "blendMode": "Overlay", + "visible": true + }, + { + "name": "Inflamed Tissue", + "effectController": { + "type": "CellNoise", + "animate": true, + "time": 4.4, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cNoiseFrequency": 7.6, + "cNoiseSphereEnable": false, + "cNoiseGraphEnable": false, + "cColorBalanceShadowsR": -0.25, + "cColorBalanceShadowsG": -1, + "cColorBalanceShadowsB": -1, + "cColorBalanceMidtonesR": 0.85, + "cColorBalanceMidtonesG": -0.65, + "cColorBalanceMidtonesB": -0.82, + "cColorBalanceHighlightsR": 1, + "cColorBalanceHighlightsG": 0.2, + "cColorBalanceHighlightsB": 0.05 + }, + "opacity": 0.44, + "blendMode": "Multiply", + "visible": true + }, + { + "name": "Oily Inflammation Sheen", + "effectController": { + "type": "Caustics", + "animate": true, + "time": 0.8, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cScale": 2.8, + "cSpeed": 0.18, + "cColor": 0.5, + "cColorBalanceShadowsR": -0.5, + "cColorBalanceShadowsG": -0.95, + "cColorBalanceShadowsB": -0.65, + "cColorBalanceMidtonesR": 0.6, + "cColorBalanceMidtonesG": 0.05, + "cColorBalanceMidtonesB": 0, + "cColorBalanceHighlightsR": 1, + "cColorBalanceHighlightsG": 0.78, + "cColorBalanceHighlightsB": 0.62 + }, + "opacity": 0.27, + "blendMode": "Screen", + "visible": true + } + ], + "gradient": { + "enabled": true, + "intensity": 0.8, + "stops": [ + { + "position": 0, + "color": "#2b0f11" + }, + { + "position": 0.28, + "color": "#6f252e" + }, + { + "position": 0.55, + "color": "#b64858" + }, + { + "position": 0.8, + "color": "#de7d83" + }, + { + "position": 1, + "color": "#ffd1c8" + } + ] + } +} diff --git a/presets/projects/squid-tentacles-hero.json b/presets/projects/squid-tentacles-hero.json new file mode 100644 index 0000000..4e4cbc9 --- /dev/null +++ b/presets/projects/squid-tentacles-hero.json @@ -0,0 +1,35 @@ +{ + "version": 1, + "resolution": 1024, + "layers": [ + { + "name": "Squid Tentacles Hero", + "effectController": { + "type": "GLSL:Squid Tentacles Hero", + "animate": true, + "time": 0, + "resolution": "1024", + "polarConversion": false, + "tiling": false, + "normalMap": false + }, + "opacity": 1, + "blendMode": "Normal", + "visible": true + } + ], + "gradient": { + "enabled": false, + "intensity": 1, + "stops": [ + { + "position": 0, + "color": "#000000" + }, + { + "position": 1, + "color": "#ffffff" + } + ] + } +} diff --git a/presets/projects/squid-tentacles-v2.json b/presets/projects/squid-tentacles-v2.json new file mode 100644 index 0000000..ef0474f --- /dev/null +++ b/presets/projects/squid-tentacles-v2.json @@ -0,0 +1,252 @@ +{ + "version": 1, + "resolution": 512, + "layers": [ + { + "name": "Tentacle Body A", + "effectController": { + "type": "BrushStroke", + "animate": true, + "time": 1.6, + "resolution": "512", + "polarConversion": false, + "tiling": false, + "normalMap": false, + "cWidth": 0.62, + "cStrength": 0.86, + "cAlpha": 0.92, + "cAngle": 0.18, + "cAmplitude": 0.34, + "cBrushStrokeX1": -0.78, + "cBrushStrokeY1": 0.94, + "cBrushStrokeX2": 0.22, + "cBrushStrokeY2": -0.82, + "cColor": 0.72, + "cColorBalanceShadowsR": -0.95, + "cColorBalanceShadowsG": -0.58, + "cColorBalanceShadowsB": 0.08, + "cColorBalanceMidtonesR": 0.48, + "cColorBalanceMidtonesG": -0.15, + "cColorBalanceMidtonesB": 0.52, + "cColorBalanceHighlightsR": 0.95, + "cColorBalanceHighlightsG": 0.3, + "cColorBalanceHighlightsB": 0.78 + }, + "opacity": 1, + "blendMode": "Normal", + "visible": true + }, + { + "name": "Tentacle Body B", + "effectController": { + "type": "BrushStroke", + "animate": true, + "time": 2.3, + "resolution": "512", + "polarConversion": false, + "tiling": false, + "normalMap": false, + "cWidth": 0.54, + "cStrength": 0.8, + "cAlpha": 0.84, + "cAngle": 0.62, + "cAmplitude": 0.46, + "cBrushStrokeX1": -0.12, + "cBrushStrokeY1": 1, + "cBrushStrokeX2": 0.78, + "cBrushStrokeY2": -0.7, + "cColor": 0.76, + "cColorBalanceShadowsR": -0.98, + "cColorBalanceShadowsG": -0.6, + "cColorBalanceShadowsB": 0.12, + "cColorBalanceMidtonesR": 0.58, + "cColorBalanceMidtonesG": -0.08, + "cColorBalanceMidtonesB": 0.58, + "cColorBalanceHighlightsR": 1, + "cColorBalanceHighlightsG": 0.38, + "cColorBalanceHighlightsB": 0.82 + }, + "opacity": 0.82, + "blendMode": "Screen", + "visible": true + }, + { + "name": "Tentacle Body C", + "effectController": { + "type": "BrushStroke", + "animate": true, + "time": 3.1, + "resolution": "512", + "polarConversion": false, + "tiling": false, + "normalMap": false, + "cWidth": 0.42, + "cStrength": 0.78, + "cAlpha": 0.72, + "cAngle": 0.41, + "cAmplitude": 0.51, + "cBrushStrokeX1": 0.42, + "cBrushStrokeY1": 0.96, + "cBrushStrokeX2": -0.62, + "cBrushStrokeY2": -0.56, + "cColor": 0.68, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.68, + "cColorBalanceShadowsB": -0.02, + "cColorBalanceMidtonesR": 0.38, + "cColorBalanceMidtonesG": -0.22, + "cColorBalanceMidtonesB": 0.46, + "cColorBalanceHighlightsR": 0.92, + "cColorBalanceHighlightsG": 0.22, + "cColorBalanceHighlightsB": 0.72 + }, + "opacity": 0.72, + "blendMode": "Screen", + "visible": true + }, + { + "name": "Suction Cups", + "effectController": { + "type": "Bubbles", + "animate": true, + "time": 5.6, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.18, + "cWidth": 0.26, + "cThickness": 0.96, + "cColor": 0.38, + "cBubblesVariation": 2, + "cColorBalanceShadowsR": -0.85, + "cColorBalanceShadowsG": -0.72, + "cColorBalanceShadowsB": 0.12, + "cColorBalanceMidtonesR": 0.25, + "cColorBalanceMidtonesG": -0.08, + "cColorBalanceMidtonesB": 0.32, + "cColorBalanceHighlightsR": 0.95, + "cColorBalanceHighlightsG": 0.58, + "cColorBalanceHighlightsB": 0.78 + }, + "opacity": 0.52, + "blendMode": "Overlay", + "visible": true + }, + { + "name": "Cup Interior Glow", + "effectController": { + "type": "Bubbles", + "animate": true, + "time": 4.2, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.1, + "cWidth": 0.1, + "cThickness": 0.78, + "cColor": 0.92, + "cBubblesVariation": 1, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.5, + "cColorBalanceShadowsB": 0.2, + "cColorBalanceMidtonesR": -0.42, + "cColorBalanceMidtonesG": 0.22, + "cColorBalanceMidtonesB": 0.72, + "cColorBalanceHighlightsR": 0.08, + "cColorBalanceHighlightsG": 0.95, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.24, + "blendMode": "SoftLight", + "visible": true + }, + { + "name": "Wet Membrane", + "effectController": { + "type": "WaterTurbulence", + "animate": true, + "time": 1.2, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cScale": 0.24, + "cIntensity": 0.34, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.7, + "cColorBalanceShadowsB": 0.08, + "cColorBalanceMidtonesR": -0.4, + "cColorBalanceMidtonesG": 0.18, + "cColorBalanceMidtonesB": 0.74, + "cColorBalanceHighlightsR": 0.36, + "cColorBalanceHighlightsG": 0.92, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.22, + "blendMode": "SoftLight", + "visible": true + }, + { + "name": "Bioluminescent Veins", + "effectController": { + "type": "Energy", + "animate": true, + "time": 7.1, + "resolution": "512", + "polarConversion": false, + "tiling": false, + "normalMap": false, + "cPower": 0.26, + "cDensity": 0.34, + "cThickness": 0.3, + "cScale": 0.18, + "cFrequency": 0.22, + "cColor": 0.84, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.2, + "cColorBalanceShadowsB": 0.18, + "cColorBalanceMidtonesR": -0.7, + "cColorBalanceMidtonesG": 0.58, + "cColorBalanceMidtonesB": 0.92, + "cColorBalanceHighlightsR": 0.22, + "cColorBalanceHighlightsG": 1, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.2, + "blendMode": "Add", + "visible": true + } + ], + "gradient": { + "enabled": true, + "intensity": 0.92, + "stops": [ + { + "position": 0, + "color": "#02040a" + }, + { + "position": 0.18, + "color": "#120b24" + }, + { + "position": 0.45, + "color": "#44205d" + }, + { + "position": 0.72, + "color": "#b43886" + }, + { + "position": 0.9, + "color": "#38d0de" + }, + { + "position": 1, + "color": "#dffcff" + } + ] + } +} diff --git a/presets/projects/squid-tentacles.json b/presets/projects/squid-tentacles.json new file mode 100644 index 0000000..2b0a987 --- /dev/null +++ b/presets/projects/squid-tentacles.json @@ -0,0 +1,157 @@ +{ + "version": 1, + "resolution": 512, + "layers": [ + { + "name": "Tentacle Flesh", + "effectController": { + "type": "Trabeculum", + "animate": true, + "time": 2.8, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cHeightScale": 2, + "cRadialMask": 1, + "cDensity": 0.92, + "cScale": 0.42, + "cIntensity": 0.84, + "cTrabeculumVariation": 2, + "cCameraTilt": 0.62, + "cCameraPan": 0.46, + "cColor": 0.78, + "cColorBalanceShadowsR": -0.88, + "cColorBalanceShadowsG": -0.42, + "cColorBalanceShadowsB": 0.35, + "cColorBalanceMidtonesR": 0.68, + "cColorBalanceMidtonesG": -0.15, + "cColorBalanceMidtonesB": 0.72, + "cColorBalanceHighlightsR": 1, + "cColorBalanceHighlightsG": 0.32, + "cColorBalanceHighlightsB": 0.9, + "cToonEnable": false, + "cToonDark": 0.8, + "cToonLight": 0.95 + }, + "opacity": 1, + "blendMode": "Normal", + "visible": true + }, + { + "name": "Suction Cup Field", + "effectController": { + "type": "Bubbles", + "animate": true, + "time": 5.1, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cRadius": 0.24, + "cWidth": 0.32, + "cThickness": 0.88, + "cColor": 0.64, + "cBubblesVariation": 2, + "cColorBalanceShadowsR": -0.55, + "cColorBalanceShadowsG": -0.5, + "cColorBalanceShadowsB": 0.25, + "cColorBalanceMidtonesR": 0.75, + "cColorBalanceMidtonesG": -0.1, + "cColorBalanceMidtonesB": 0.55, + "cColorBalanceHighlightsR": 1, + "cColorBalanceHighlightsG": 0.55, + "cColorBalanceHighlightsB": 0.88 + }, + "opacity": 0.58, + "blendMode": "Overlay", + "visible": true + }, + { + "name": "Bioluminescent Veins", + "effectController": { + "type": "Energy", + "animate": true, + "time": 7.4, + "resolution": "512", + "polarConversion": false, + "tiling": false, + "normalMap": false, + "cPower": 0.42, + "cDensity": 0.72, + "cThickness": 0.62, + "cScale": 0.36, + "cFrequency": 0.95, + "cColor": 0.94, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.15, + "cColorBalanceShadowsB": 0.35, + "cColorBalanceMidtonesR": -0.65, + "cColorBalanceMidtonesG": 0.75, + "cColorBalanceMidtonesB": 1, + "cColorBalanceHighlightsR": 0.25, + "cColorBalanceHighlightsG": 1, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.34, + "blendMode": "Add", + "visible": true + }, + { + "name": "Wet Iridescent Sheen", + "effectController": { + "type": "WaterTurbulence", + "animate": true, + "time": 1.3, + "resolution": "512", + "polarConversion": false, + "tiling": true, + "normalMap": false, + "cScale": 0.66, + "cIntensity": 0.61, + "cColorBalanceShadowsR": -1, + "cColorBalanceShadowsG": -0.65, + "cColorBalanceShadowsB": 0.1, + "cColorBalanceMidtonesR": -0.45, + "cColorBalanceMidtonesG": 0.35, + "cColorBalanceMidtonesB": 0.95, + "cColorBalanceHighlightsR": 0.4, + "cColorBalanceHighlightsG": 0.95, + "cColorBalanceHighlightsB": 1 + }, + "opacity": 0.3, + "blendMode": "Screen", + "visible": true + } + ], + "gradient": { + "enabled": true, + "intensity": 0.88, + "stops": [ + { + "position": 0, + "color": "#02050f" + }, + { + "position": 0.22, + "color": "#1a0c36" + }, + { + "position": 0.5, + "color": "#4b145f" + }, + { + "position": 0.72, + "color": "#b32c88" + }, + { + "position": 0.9, + "color": "#33d6e9" + }, + { + "position": 1, + "color": "#d9ffff" + } + ] + } +} diff --git a/shader-defs.js b/shader-defs.js index 6a21570..dcfe1b2 100644 --- a/shader-defs.js +++ b/shader-defs.js @@ -4147,3 +4147,321 @@ void main(){ gl_FragColor=vec4(col,1); }`; + +// 53. MOON SURFACE HERO +SHADERS.moon_surface_hero=` +uniform float time; +uniform vec2 resolution; +${NOISE_LIB} + +vec3 skyColor(vec3 rd){ + float h=max(rd.y*0.5+0.5,0.0); + vec3 col=mix(vec3(0.004,0.006,0.012),vec3(0.02,0.028,0.045),pow(h,0.8)); + vec2 suv=rd.xz/(abs(rd.y)+0.2); + vec2 gid=floor(suv*120.0); + float star=step(0.9988,hash21(gid)); + col+=vec3(star)*0.8*(1.0-smoothstep(-0.1,0.25,rd.y)); + return col; +} + +float craterShape(vec2 p,vec2 c,float r){ + float d=length(p-c); + float x=d/max(r,0.0001); + float bowl=-(1.0-smoothstep(0.0,1.0,x))*pow(1.0-clamp(x,0.0,1.0),1.65); + float rim=exp(-pow((d-r)/(r*0.22),2.0))*0.52; + float ejecta=exp(-pow(d/(r*2.6),2.0))*0.05; + return bowl*0.95+rim+ejecta; +} + +float craterLayer(vec2 p,float density,float rMin,float rMax,float seed,float weight){ + vec2 sp=p*density; + vec2 cell=floor(sp); + float h=0.0; + for(int j=-1;j<=1;j++){ + for(int i=-1;i<=1;i++){ + vec2 id=cell+vec2(float(i),float(j)); + float occ=step(0.34,hash21(id+vec2(seed,seed*1.27))); + vec2 jitter=vec2( + hash21(id+vec2(seed+2.7,seed+8.1)), + hash21(id+vec2(seed+13.4,seed+17.9)) + )-0.5; + vec2 center=(id+0.5+jitter*0.72)/density; + float rr=mix(rMin,rMax,hash21(id+vec2(seed+23.0,seed+29.0))); + h+=craterShape(p,center,rr)*occ*weight; + } + } + return h; +} + +float lunarTerrain(vec2 p){ + vec2 q=p; + q*=rot2(0.18); + float base=fbm(vec3(q*2.4,1.0),5)*0.05; + base+=fbm(vec3(q*8.0,4.0),3)*0.012; + base-=abs(fbm(vec3(q*5.8,7.5),4))*0.012; + + float craters=0.0; + craters+=craterShape(q,vec2(-0.28,0.22),0.52)*0.13; + craters+=craterShape(q,vec2(0.34,-0.18),0.27)*0.09; + craters+=craterLayer(q,1.55,0.12,0.32,11.0,0.12); + craters+=craterLayer(q+4.2,3.1,0.05,0.13,23.0,0.07); + craters+=craterLayer(q-7.1,7.0,0.018,0.05,37.0,0.028); + + return base+craters; +} + +vec3 terrainNormal(vec2 p){ + vec2 e=vec2(0.0035,0.0); + float hx=lunarTerrain(p+e.xy)-lunarTerrain(p-e.xy); + float hz=lunarTerrain(p+e.yx)-lunarTerrain(p-e.yx); + return normalize(vec3(-hx/(2.0*e.x),1.0,-hz/(2.0*e.x))); +} + +float terrainShadow(vec3 p,vec3 sunDir){ + float res=1.0; + float t=0.03; + for(int i=0;i<22;i++){ + vec3 pos=p+sunDir*t; + float h=pos.y-lunarTerrain(pos.xz)-0.0015; + res=min(res,clamp(8.0*h/t,0.0,1.0)); + if(res<0.01)break; + t+=0.05+float(i)*0.02; + } + return clamp(res,0.0,1.0); +} + +vec3 getRay(vec2 uv,vec3 ro,vec3 ta){ + vec3 ww=normalize(ta-ro); + vec3 uu=normalize(cross(vec3(0.0,1.0,0.0),ww)); + vec3 vv=cross(ww,uu); + return normalize(uu*uv.x+vv*uv.y+ww*1.9); +} + +bool traceTerrain(vec3 ro,vec3 rd,out vec3 hit,out float tHit){ + float t=0.0; + float prevT=0.0; + for(int i=0;i<140;i++){ + vec3 pos=ro+rd*t; + float h=pos.y-lunarTerrain(pos.xz); + if(h<0.001){ + float a=prevT; + float b=t; + for(int j=0;j<5;j++){ + float m=0.5*(a+b); + vec3 mp=ro+rd*m; + float mh=mp.y-lunarTerrain(mp.xz); + if(mh>0.0)a=m; + else b=m; + } + tHit=b; + hit=ro+rd*tHit; + return true; + } + prevT=t; + t+=max(0.012,h*0.55); + if(t>5.5)break; + } + return false; +} + +void main(){ + vec2 uv=(gl_FragCoord.xy-0.5*resolution.xy)/resolution.y; + float glide=time*0.02; + vec3 ro=vec3(-0.12+sin(time*0.07)*0.08,0.34,-1.58+glide); + vec3 ta=vec3(0.04,0.03,0.46+glide); + vec3 rd=getRay(uv,ro,ta); + + vec3 hit; + float tHit; + vec3 col=skyColor(rd); + + if(traceTerrain(ro,rd,hit,tHit)){ + vec3 n=terrainNormal(hit.xz); + vec3 sunDir=normalize(vec3(-0.55,0.72,-0.42)); + float shadow=terrainShadow(hit+n*0.002,sunDir); + float diff=max(dot(n,sunDir),0.0)*shadow; + float hemi=0.55+0.45*n.y; + float spec=pow(max(dot(reflect(-sunDir,n),-rd),0.0),28.0)*shadow; + float fres=pow(1.0-max(dot(n,-rd),0.0),3.0); + float dust=fbm(vec3(hit.xz*24.0,3.2),4)*0.5+0.5; + float h=lunarTerrain(hit.xz); + float tone=clamp(h*6.5+0.5,0.0,1.0); + float cavity=clamp(0.55-h*12.0,0.0,1.0); + + vec3 albedo=mix(vec3(0.19,0.19,0.2),vec3(0.6,0.62,0.66),hemi*0.7+tone*0.3); + albedo*=mix(0.92,1.08,dust); + + vec3 lightCol=vec3(1.0,0.985,0.96); + vec3 coldSpec=vec3(0.56,0.74,1.0); + col=albedo*(0.16+diff*0.92); + col+=lightCol*spec*0.16; + col+=coldSpec*fres*0.09; + col*=1.0-cavity*0.08; + col=mix(col,skyColor(rd),smoothstep(3.6,5.2,tHit)); + } + + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.95)),1.0); +}`; + +// 54. SQUID TENTACLES HERO +SHADERS.squid_tentacles_hero=` +uniform float time; +uniform vec2 resolution; +${NOISE_LIB} + +vec2 bez4(vec2 a,vec2 b,vec2 c,vec2 d,float t){ + float it=1.0-t; + return it*it*it*a+3.0*it*it*t*b+3.0*it*t*t*c+t*t*t*d; +} + +vec2 bez4d(vec2 a,vec2 b,vec2 c,vec2 d,float t){ + float it=1.0-t; + return 3.0*it*it*(b-a)+6.0*it*t*(c-b)+3.0*t*t*(d-c); +} + +float tentacleField( + vec2 p, + vec2 a,vec2 b,vec2 c,vec2 d, + float width,float sideSign,float seed, + inout float bestT,inout vec2 bestN,inout float cups,inout float glow +){ + float best=1e5; + for(int i=0;i<52;i++){ + float t=float(i)/51.0; + vec2 pos=bez4(a,b,c,d,t); + vec2 der=bez4d(a,b,c,d,t); + vec2 tangent=normalize(der+vec2(0.0001,0.0)); + vec2 normal=vec2(-tangent.y,tangent.x)*sideSign; + float taper=mix(width,width*0.14,pow(t,0.82)); + float dBody=length(p-pos)-taper; + if(dBody0.0){ + vec3 baseA=vec3(0.08,0.02,0.12); + vec3 baseB=vec3(0.42,0.05,0.32); + vec3 baseC=vec3(0.88,0.18,0.5); + vec3 bodyCol=mix(baseA,baseB,1.0-bodyT); + bodyCol=mix(bodyCol,baseC,pow(1.0-bodyT,1.5)*0.55); + + vec3 n=normalize(vec3(bodyN*1.45,1.0)); + vec3 lightDir=normalize(vec3(-0.38,0.74,0.56)); + vec3 viewDir=vec3(0.0,0.0,1.0); + float diff=max(dot(n,lightDir),0.0); + float spec=pow(max(dot(reflect(-lightDir,n),viewDir),0.0),24.0); + float rim=pow(1.0-max(dot(n,viewDir),0.0),2.4); + + vec3 fleshyCup=vec3(1.0,0.65,0.82)*cups*0.75; + vec3 bioGlow=vec3(0.1,0.95,1.0)*glow*0.45; + vec3 sheen=vec3(0.95,0.98,1.0)*spec*0.34; + vec3 wetEdge=vec3(0.18,0.65,0.92)*rim*0.12; + + vec3 lit=bodyCol*(0.14+diff*0.95); + lit+=fleshyCup+bioGlow+sheen+wetEdge; + col=mix(col,lit,mask); + } + + float vignette=smoothstep(1.9,0.35,length(p)); + col*=vignette; + gl_FragColor=vec4(pow(max(col,0.0),vec3(0.95)),1.0); +}`; diff --git a/src/custom-shader-registry.js b/src/custom-shader-registry.js index 765da57..ca7ad76 100644 --- a/src/custom-shader-registry.js +++ b/src/custom-shader-registry.js @@ -57,6 +57,8 @@ export const CUSTOM_SHADER_CATALOG = [ { name: "Void Tendril", key: "void_tendril", file: "150-void-tendril" }, { name: "Summoning Circle", key: "summoning", file: "151-summoning-circle" }, { name: "Lightning Smite", key: "lightning_smite", file: "152-lightning-smite" }, + { name: "Moon Surface Hero", key: "moon_surface_hero", file: "153-moon-surface-hero" }, + { name: "Squid Tentacles Hero", key: "squid_tentacles_hero", file: "154-squid-tentacles-hero" }, ]; // Prefix used for custom shader effect types in the layer system