You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							253 lines
						
					
					
						
							7.2 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							253 lines
						
					
					
						
							7.2 KiB
						
					
					
				
								<!DOCTYPE html>
							 | 
						|
								<html lang="en">
							 | 
						|
								  <head>
							 | 
						|
								    <title>Three.js - postprocessing - 3DLUT w/loader</title>
							 | 
						|
								    <meta charset="utf-8">
							 | 
						|
								    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
							 | 
						|
								    <style>
							 | 
						|
								    html, body {
							 | 
						|
								      min-height: 100%;
							 | 
						|
								      font-family: monospace;
							 | 
						|
								      background: #222;
							 | 
						|
								      color: white;
							 | 
						|
								    }
							 | 
						|
								    canvas {
							 | 
						|
								      min-width: 512px;
							 | 
						|
								      min-height: 64px;
							 | 
						|
								      image-rendering: pixelated;
							 | 
						|
								    }
							 | 
						|
								    #cube {
							 | 
						|
								      max-width: calc(100% - 20px);
							 | 
						|
								      overflow: auto;
							 | 
						|
								    }
							 | 
						|
								  </style>
							 | 
						|
								  </head>
							 | 
						|
								  <body>
							 | 
						|
								    <h1>Adobe LUT to PNG converter</h1>
							 | 
						|
								    <p>Drag and drop a LUT file here</p>
							 | 
						|
								    <div>size:<input id="size" type="number" value="8" min="2" max="64"/></div>
							 | 
						|
								    <p id="result"></p>
							 | 
						|
								    <p><button type="button">Save...</button></p>
							 | 
						|
								    <div id="cube"><canvas id="c"></canvas></div>
							 | 
						|
								  </body>
							 | 
						|
								<script type="importmap">
							 | 
						|
								{
							 | 
						|
								  "imports": {
							 | 
						|
								    "three": "../../build/three.module.js",
							 | 
						|
								    "three/addons/": "../../examples/jsm/"
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								</script>
							 | 
						|
								
							 | 
						|
								<script type="module">
							 | 
						|
								import * as THREE from 'three';
							 | 
						|
								import * as lutParser from './resources/lut-reader.js';
							 | 
						|
								import * as dragAndDrop from './resources/drag-and-drop.js';
							 | 
						|
								import {EffectComposer} from 'three/addons/postprocessing/EffectComposer.js';
							 | 
						|
								import {RenderPass} from 'three/addons/postprocessing/RenderPass.js';
							 | 
						|
								import {ShaderPass} from 'three/addons/postprocessing/ShaderPass.js';
							 | 
						|
								
							 | 
						|
								function main() {
							 | 
						|
								  const canvas = document.querySelector('#c');
							 | 
						|
								  const renderer = new THREE.WebGLRenderer({canvas});
							 | 
						|
								
							 | 
						|
								  const makeIdentityLutTexture = function() {
							 | 
						|
								    const identityLUT = new Uint8Array([
							 | 
						|
								        0,   0,   0, 255,  // black
							 | 
						|
								      255,   0,   0, 255,  // red
							 | 
						|
								        0,   0, 255, 255,  // blue
							 | 
						|
								      255,   0, 255, 255,  // magenta
							 | 
						|
								        0, 255,   0, 255,  // green
							 | 
						|
								      255, 255,   0, 255,  // yellow
							 | 
						|
								        0, 255, 255, 255,  // cyan
							 | 
						|
								      255, 255, 255, 255,  // white
							 | 
						|
								    ]);
							 | 
						|
								
							 | 
						|
								    return function(filter) {
							 | 
						|
								      const texture = new THREE.DataTexture(identityLUT, 4, 2);
							 | 
						|
								      texture.minFilter = filter;
							 | 
						|
								      texture.magFilter = filter;
							 | 
						|
								      texture.needsUpdate = true;
							 | 
						|
								      texture.flipY = false;
							 | 
						|
								      return texture;
							 | 
						|
								    };
							 | 
						|
								  }();
							 | 
						|
								
							 | 
						|
								  const sceneBG = new THREE.Scene();
							 | 
						|
								  const cameraBG = new THREE.OrthographicCamera(-1, 1, 1, -1, -1, 1);
							 | 
						|
								
							 | 
						|
								  const ctx = document.createElement('canvas').getContext('2d');
							 | 
						|
								  function drawColorCubeImage(ctx, size) {
							 | 
						|
								    const canvas = ctx.canvas;
							 | 
						|
								    canvas.width = size * size;
							 | 
						|
								    canvas.height = size;
							 | 
						|
								
							 | 
						|
								    for (let zz = 0; zz < size; ++zz) {
							 | 
						|
								      for (let yy = 0; yy < size; ++yy) {
							 | 
						|
								        for (let xx = 0; xx < size; ++xx) {
							 | 
						|
								          const r = Math.floor(xx / (size - 1) * 255);
							 | 
						|
								          const g = Math.floor(yy / (size - 1) * 255);
							 | 
						|
								          const b = Math.floor(zz / (size - 1) * 255);
							 | 
						|
								          ctx.fillStyle = `rgb(${r},${g},${b})`;
							 | 
						|
								          ctx.fillRect(zz * size + xx, yy, 1, 1);
							 | 
						|
								        }
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const idTexture = new THREE.CanvasTexture(ctx.canvas);
							 | 
						|
								  idTexture.magFilter = THREE.NearestFilter;
							 | 
						|
								  idTexture.minFilter = THREE.NearestFilter;
							 | 
						|
								
							 | 
						|
								  {
							 | 
						|
								    const planeGeo = new THREE.PlaneGeometry(2, 2);
							 | 
						|
								    const planeMat = new THREE.MeshBasicMaterial({
							 | 
						|
								      map: idTexture,
							 | 
						|
								      depthTest: false,
							 | 
						|
								    });
							 | 
						|
								    sceneBG.add(new THREE.Mesh(planeGeo, planeMat));
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const lutShader = {
							 | 
						|
								    uniforms: {
							 | 
						|
								      tDiffuse: { value: null },
							 | 
						|
								      lutMap:  { value: null },
							 | 
						|
								      lutMapSize: { value: 1, },
							 | 
						|
								    },
							 | 
						|
								    vertexShader: `
							 | 
						|
								      varying vec2 vUv;
							 | 
						|
								      void main() {
							 | 
						|
								        vUv = uv;
							 | 
						|
								        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
							 | 
						|
								      }
							 | 
						|
								    `,
							 | 
						|
								    fragmentShader: `
							 | 
						|
								      #include <common>
							 | 
						|
								
							 | 
						|
								      #define FILTER_LUT true
							 | 
						|
								
							 | 
						|
								      uniform sampler2D tDiffuse;
							 | 
						|
								      uniform sampler2D lutMap;
							 | 
						|
								      uniform float lutMapSize;
							 | 
						|
								
							 | 
						|
								      varying vec2 vUv;
							 | 
						|
								
							 | 
						|
								      vec4 sampleAs3DTexture(sampler2D tex, vec3 texCoord, float size) {
							 | 
						|
								        float sliceSize = 1.0 / size;                  // space of 1 slice
							 | 
						|
								        float slicePixelSize = sliceSize / size;       // space of 1 pixel
							 | 
						|
								        float width = size - 1.0;
							 | 
						|
								        float sliceInnerSize = slicePixelSize * width; // space of size pixels
							 | 
						|
								        float zSlice0 = floor( texCoord.z * width);
							 | 
						|
								        float zSlice1 = min( zSlice0 + 1.0, width);
							 | 
						|
								        float xOffset = slicePixelSize * 0.5 + texCoord.x * sliceInnerSize;
							 | 
						|
								        float yRange = (texCoord.y * width + 0.5) / size;
							 | 
						|
								        float s0 = xOffset + (zSlice0 * sliceSize);
							 | 
						|
								
							 | 
						|
								        #ifdef FILTER_LUT
							 | 
						|
								
							 | 
						|
								          float s1 = xOffset + (zSlice1 * sliceSize);
							 | 
						|
								          vec4 slice0Color = texture2D(tex, vec2(s0, yRange));
							 | 
						|
								          vec4 slice1Color = texture2D(tex, vec2(s1, yRange));
							 | 
						|
								          float zOffset = mod(texCoord.z * width, 1.0);
							 | 
						|
								          return mix(slice0Color, slice1Color, zOffset);
							 | 
						|
								
							 | 
						|
								        #else
							 | 
						|
								
							 | 
						|
								          return texture2D(tex, vec2( s0, yRange));
							 | 
						|
								
							 | 
						|
								        #endif
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      void main() {
							 | 
						|
								        vec4 originalColor = texture2D(tDiffuse, vUv);
							 | 
						|
								        gl_FragColor = sampleAs3DTexture(lutMap, originalColor.xyz, lutMapSize);
							 | 
						|
								      }
							 | 
						|
								    `,
							 | 
						|
								  };
							 | 
						|
								
							 | 
						|
								  const effectLUT = new ShaderPass(lutShader);
							 | 
						|
								  effectLUT.renderToScreen = true;
							 | 
						|
								
							 | 
						|
								  const renderBG = new RenderPass(sceneBG, cameraBG);
							 | 
						|
								
							 | 
						|
								  const rtParameters = {
							 | 
						|
								    minFilter: THREE.NearestFilter,
							 | 
						|
								    magFilter: THREE.NearestFilter
							 | 
						|
								  };
							 | 
						|
								  const composer = new EffectComposer(renderer, new THREE.WebGLRenderTarget(1, 1, rtParameters));
							 | 
						|
								
							 | 
						|
								  composer.addPass(renderBG);
							 | 
						|
								  composer.addPass(effectLUT);
							 | 
						|
								
							 | 
						|
								  let name = 'identity';
							 | 
						|
								  const lutTexture = makeIdentityLutTexture(THREE.LinearFilter);
							 | 
						|
								  effectLUT.uniforms.lutMap.value = lutTexture;
							 | 
						|
								  effectLUT.uniforms.lutMapSize.value = 2;
							 | 
						|
								
							 | 
						|
								  const sizeElem = document.querySelector('#size');
							 | 
						|
								  sizeElem.addEventListener('change', render);
							 | 
						|
								
							 | 
						|
								  function render() {
							 | 
						|
								    const size = parseInt(sizeElem.value);
							 | 
						|
								    renderer.setSize(size * size, size, false);
							 | 
						|
								    composer.setSize(size * size, size);
							 | 
						|
								
							 | 
						|
								    drawColorCubeImage(ctx, size);
							 | 
						|
								    idTexture.needsUpdate = true;
							 | 
						|
								
							 | 
						|
								    composer.render(0);
							 | 
						|
								  }
							 | 
						|
								  render();
							 | 
						|
								
							 | 
						|
								  dragAndDrop.setup({msg: 'Drop LUT File here'});
							 | 
						|
								  dragAndDrop.onDropFile(readLUTFile);
							 | 
						|
								
							 | 
						|
								  function ext(s) {
							 | 
						|
								    const period = s.lastIndexOf('.');
							 | 
						|
								    return s.slice(period + 1);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  function readLUTFile(file) {
							 | 
						|
								    const reader = new FileReader();
							 | 
						|
								    reader.onload = (e) => {
							 | 
						|
								      const type = ext(file.name);
							 | 
						|
								      const lut = lutParser.lutTo2D3Drgba8(lutParser.parse(e.target.result, type));
							 | 
						|
								
							 | 
						|
								      effectLUT.uniforms.lutMapSize.value = lut.size;
							 | 
						|
								
							 | 
						|
								      lutTexture.image.data = lut.data;
							 | 
						|
								      lutTexture.image.width = lut.size * lut.size;
							 | 
						|
								      lutTexture.image.height = lut.size;
							 | 
						|
								      lutTexture.needsUpdate = true;
							 | 
						|
								
							 | 
						|
								      render();
							 | 
						|
								      name = `${file.name || lut.name || 'untitled'}`;
							 | 
						|
								      document.querySelector('#result').textContent = `loaded: ${name}`;
							 | 
						|
								    };
							 | 
						|
								
							 | 
						|
								    reader.readAsText(file);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const saveData = (function() {
							 | 
						|
								    const a = document.createElement('a');
							 | 
						|
								    document.body.appendChild(a);
							 | 
						|
								    a.style.display = 'none';
							 | 
						|
								    return function saveData(blob, fileName) {
							 | 
						|
								      const url = window.URL.createObjectURL(blob);
							 | 
						|
								      a.href = url;
							 | 
						|
								      a.download = fileName;
							 | 
						|
								      a.click();
							 | 
						|
								    };
							 | 
						|
								  }());
							 | 
						|
								
							 | 
						|
								  document.querySelector('button').addEventListener('click', () => {
							 | 
						|
								    render();
							 | 
						|
								    renderer.domElement.toBlob((blob) => {
							 | 
						|
								      saveData(blob, `${name}-s${renderer.domElement.height}.png`);
							 | 
						|
								    });
							 | 
						|
								  });
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								main();
							 | 
						|
								</script>
							 | 
						|
								</html>
							 | 
						|
								
							 |