Hi there,
I’m hoping to reproduce some of the Pepper’s Cone’s project in Three.js. Essentially, they implemented a way of distorting a 3d model so that it appears undistorted when reflected from a screen onto a cone, creating a holographic effect. Their project was done in Unity and uses shaders.
I have some experience using Three.js, but mostly used their helper methods and still have some trouble with the “guts” of WEBGL, and I’m very new to shaders.
I grabbed the generated GL shader code from Unity (compiling for OPENGL ES 2.0, since I believe that’s best for WebGL) and tried to essentially rewrite their other code in JavaScript/THREE.
I shouldn’t be surprised that nothing appears on screen given my approach, but I thought I’d reach out here before… whatever my next step would be otherwise.
I used the project set-up from https://github.com/Jam3/jam3-lesson-webgl-shader-threejs, and the index.js below represents the modified file in that project.
index.js
global.THREE = require('three');
const path = require('path');
const fs = require('fs');
const createApp = require('./createApp');
const createBunnyGeometry = require('./createBunnyGeometry');
const createLoop = require('raf-loop');
const getPixels = require("get-pixels")
//warp vars
var mapDiv = 4095; //0x3FFF;
var flipTexture = true;
var power = 1, alpha = 1;
var tabletScreenScale = new THREE.Vector3 (4, 3, 1);
var encodedMap;
var LOAD_TEX_COLOR_BIT_DEPTH = 8;
var mapColor = [];
var outTexture;
// Create our basic ThreeJS application
const {
renderer,
camera,
scene,
updateControls
} = createApp();
// Create our vertex/fragment shaders
var material = new THREE.RawShaderMaterial({
vertexShader: fs.readFileSync(path.join(__dirname, 'shader2.vert'), 'utf8'),
fragmentShader: fs.readFileSync(path.join(__dirname, 'shader2.frag'), 'utf8'),
uniforms: {
time: { type: 'f', value: 0 },
_power: { type: 'f', value: 0},
_alpha: { type: 'f', value: 0},
_TexRotationVec: { type: 'vec4', value: new THREE.Vector4()}
}
});
//get colours of each pixel
getPixels("https://raw.githubusercontent.com/roxanneluo/Pepper-s-Cone-Unity/master/Assets/Textures/IpadProDistortionCalibrationMap.png", function(err, pixels) {
if(err) {
console.log("Bad image path")
return
}
processPixelData(pixels);
})
//format pixel data into a new texture
//this function and LateUpdated are adapted from
//https://github.com/roxanneluo/Pepper-s-Cone-Unity/blob/f439adfb331b58d76c8b5dafd7a56f80b9cb6963/Assets/Scripts/WarpBase.cs
//and
//https://github.com/roxanneluo/Pepper-s-Cone-Unity/blob/f439adfb331b58d76c8b5dafd7a56f80b9cb6963/Assets/Scripts/WarpMono.cs
function processPixelData(pix){
var convertedPixels = [];
var index = 0;
//we could probably combine this and next loop
for (var i = 0; i < (pix.data.length); i+=4){
convertedPixels[index] = {
r: pix.data[i],
g: pix.data[i + 1],
b: pix.data[i + 2],
a: pix.data[i + 3]
}
index++;
}
for (var i = 0; i < convertedPixels.length; i++){
var ec = convertedPixels[i];
mapColor[i] = new THREE.Color("rgb(0, 0, 0)");
mapColor[i].r = ((ec.r << LOAD_TEX_COLOR_BIT_DEPTH) + ec.g) / mapDiv;
mapColor[i].g = ((ec.b << LOAD_TEX_COLOR_BIT_DEPTH) + ec.a) / mapDiv;
if (flipTexture){
mapColor [i].g = 1 - mapColor [i].g;
}
}
outTexture = new THREE.DataTexture( mapColor, pix.shape[0], pix.shape[1], THREE.RGBAFormat );
material.texture = outTexture;
}
function LateUpdate(){
var rot = new THREE.Quaternion();
// although the texture's rotating eulerZ degree, the uv needs to rotate -eulerZ
var m = new THREE.Matrix4();
var firstVar = new THREE.Matrix4();
firstVar.makeScale(1/tabletScreenScale.x, 1/tabletScreenScale.y, 1.0);
var secondVar = new THREE.Matrix4();
secondVar.compose(new THREE.Vector3, rot, tabletScreenScale);
m = firstVar * secondVar;
material.uniforms._TexRotationVec.value = new THREE.Vector4(m[0,0], m[0,1], m[1,0], m[1,1]);
material.uniforms._power = 1.0;
material.uniforms._alpha = 1.0;
}
// Get a nicely prepared geometry
const geometry = createBunnyGeometry({ flat: true });
// Setup our mesh
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// Time since beginning
let time = 0;
// Start our render loop
createLoop((dt) => {
LateUpdate();
// render
updateControls();
renderer.render(scene, camera);
}).start();
shader2.frag
uniform highp vec4 _TexRotationVec;
uniform sampler2D RenderedTex;
uniform sampler2D MapTex;
uniform highp float _power;
uniform highp float _alpha;
varying highp vec2 xlv_TEXCOORD0;
void main ()
{
highp vec4 tmpvar_1;
lowp vec4 col_2;
highp vec4 map_3;
highp mat2 tmpvar_4;
tmpvar_4[0].x = _TexRotationVec.x;
tmpvar_4[0].y = _TexRotationVec.z;
tmpvar_4[1].x = _TexRotationVec.y;
tmpvar_4[1].y = _TexRotationVec.w;
highp vec2 tmpvar_5;
tmpvar_5 = ((tmpvar_4 * (xlv_TEXCOORD0 - vec2(0.5, 0.5))) + vec2(0.5, 0.5));
bool tmpvar_6;
tmpvar_6 = (((
(0.001 <= tmpvar_5.x)
&&
(tmpvar_5.x <= 0.999)
) && (0.001 <= tmpvar_5.y)) && (tmpvar_5.y <= 0.999));
if (!(tmpvar_6)) {
tmpvar_1 = vec4(0.0, 0.0, 0.0, 0.0);
} else {
lowp vec4 tmpvar_7;
tmpvar_7 = texture2D (MapTex, tmpvar_5);
map_3 = tmpvar_7;
bool tmpvar_8;
tmpvar_8 = (((
(0.001 <= map_3.x)
&&
(map_3.x <= 0.999)
) && (0.001 <= map_3.y)) && (map_3.y <= 0.999));
if (!(tmpvar_8)) {
tmpvar_1 = vec4(0.0, 0.0, 0.0, 0.0);
} else {
lowp vec4 tmpvar_9;
tmpvar_9 = texture2D (RenderedTex, map_3.xy);
highp vec4 tmpvar_10;
tmpvar_10 = (_alpha * pow (tmpvar_9, vec4(_power)));
col_2 = tmpvar_10;
tmpvar_1 = col_2;
};
};
gl_FragData[0] = tmpvar_1;
}
shader2.vert
attribute vec4 _glesVertex;
attribute vec4 _glesMultiTexCoord0;
uniform highp mat4 unity_ObjectToWorld;
uniform highp mat4 unity_MatrixVP;
varying highp vec2 xlv_TEXCOORD0;
void main ()
{
highp vec4 tmpvar_1;
tmpvar_1.w = 1.0;
tmpvar_1.xyz = _glesVertex.xyz;
xlv_TEXCOORD0 = _glesMultiTexCoord0.xy;
gl_Position = (unity_MatrixVP * (unity_ObjectToWorld * tmpvar_1));
}
I’m sorry for using the forum like this–generally I try to be more informed about my problems, but I’ve been at a bit of a loss since, without help, I think this kind of rewrite is beyond my skill level.