似乎GLSL ES 3.0无法正确执行我的代码。
我写了两次相同的代码,首先是展开式的;第二个是for循环:
// Unrolled version:
float minDistance = 1000000.0;
vec3 finalColor1 = vec3(0.0);
int index1 = 0;
float distance = colorDifferenceCIE94FromRGB(pixel, colors[0]);
if(distance < minDistance) {
finalColor1 = colors[0];
minDistance = distance;
index1 = 0;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[1]);
if(distance < minDistance) {
finalColor1 = colors[1];
minDistance = distance;
index1 = 1;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[2]);
if(distance < minDistance) {
finalColor1 = colors[2];
minDistance = distance;
index1 = 2;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[3]);
if(distance < minDistance) {
finalColor1 = colors[3];
minDistance = distance;
index1 = 3;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[4]);
if(distance < minDistance) {
finalColor1 = colors[4];
minDistance = distance;
index1 = 4;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[5]);
if(distance < minDistance) {
finalColor1 = colors[5];
minDistance = distance;
index1 = 5;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[6]);
if(distance < minDistance) {
finalColor1 = colors[6];
minDistance = distance;
index1 = 6;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[7]);
if(distance < minDistance) {
finalColor1 = colors[7];
minDistance = distance;
index1 = 7;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[8]);
if(distance < minDistance) {
finalColor1 = colors[8];
minDistance = distance;
index1 = 8;
}
// For Loop version:
int index2 = 0;
vec3 finalColor2 = pixel;
minDistance = 100000.0;
for(int i=0 ; i<9 ; i++) {
distance = colorDifferenceCIE94FromRGB(pixel, colors[i]);
if(distance < minDistance) {
finalColor2 = colors[i];
minDistance = distance;
index2 = i;
}
}
gl_FragColor = vec4((uv.x < 0.5 ? finalColor1 : finalColor2), 1.0);
屏幕左侧应与屏幕右侧完全相同,但不是(至少在我的Macbook Pro Unibody 2012 OS X 10.14.4上)。为什么?
我在问题的底部提供了一个摘要(和一个demo project)来显示该错误。
代码使用Paper.js在画布上绘制一个红色圆圈,然后将该画布赋予Three.js CanvasTexture,该画布将应用于全屏四边形(平面网格)。
片段着色器计算(给定纹理的)像素颜色与一组颜色之间的距离;然后呈现最接近的颜色。该操作执行两次,一次是在已展开版本中/依次执行,一次是在for循环中执行。第一个版本的结果显示在屏幕左侧,第二个版本的显示在屏幕右侧。
结果应该完全相同,但是奇怪的是事实并非如此。为什么?
您可以在第157行中取消注释:
colors[8] = vec3(0.8, 0.4, 0.1);
(或第148至152行)两个代码的执行方式相似。
这是我在计算机上得到的:
左边的白色方块是paper.js画布,右边的黑色方块是three.js场景;第二个圆圈也应该完全是红色。
<!DOCTYPE html>
<html>
<head>
<title>Dynamic array glsl test</title>
<meta charset="UTF-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/103/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-full.min.js"></script>
<style>
#three,
#paper {
width: 100px;
height: 100px;
}
#three {
/*display: none;*/
}
#paper {
/*display: none;*/
}
</style>
</head>
<body>
<canvas id="paper"></canvas>
<canvas id="three"></canvas>
<script type="application/glsl" id="fragmentShader2">
varying vec2 vUV;
uniform sampler2D textureSampler;
uniform vec2 screenResolution;
uniform vec2 textureResolution;
void main(void) {
vec2 uv = gl_FragCoord.xy / screenResolution.xy - 0.5; // [-0.5, 0.5]
float screenRatio = screenResolution.x / screenResolution.y;
float textureRatio = textureResolution.x / textureResolution.y;
vec2 textureUV = textureRatio > screenRatio ? vec2(uv.x, uv.y * textureRatio / screenRatio) : vec2(uv.x / textureRatio * screenRatio, uv.y);
gl_FragColor = texture2D(textureSampler, textureUV + 0.5);
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<script type="application/glsl" id="fragmentShader">
precision highp float;
varying vec2 vUV;
uniform sampler2D textureSampler;
uniform vec2 screenResolution;
uniform vec2 textureResolution;
uniform float hueRotation;
vec3 colors[9];
#define PI 3.1415926535897932384626433832795
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
vec4 hsv2rgb(vec4 c) {
return vec4(hsv2rgb(c.xyz), c.w);
}
vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec4 rgb2hsv(vec4 c) {
return vec4(rgb2hsv(c.xyz), c.w);
}
vec4 rotateHue(vec4 c, float angle) {
vec4 chsv = rgb2hsv(c);
chsv.x = mod(chsv.x + angle, 1.0);
return hsv2rgb(chsv);
}
vec3 mixColors(vec3 c1, vec3 c2) {
return sqrt(0.5 * c1 * c1 + 0.5 * c2 * c2);
}
vec3 mixColors(vec3 c1, vec3 c2, vec3 c3) {
return sqrt( (c1 * c1 / 3.0) + (c2 * c2 / 3.0) + (c3 * c3 / 3.0));
}
vec3 rgb2xyz(vec3 rgb) {
rgb.r = rgb.r > 0.04045 ? pow( ( rgb.r + 0.055 ) / 1.055, 2.4) : rgb.r / 12.92;
rgb.g = rgb.g > 0.04045 ? pow( ( rgb.g + 0.055 ) / 1.055, 2.4) : rgb.g / 12.92;
rgb.b = rgb.b > 0.04045 ? pow( ( rgb.b + 0.055 ) / 1.055, 2.4) : rgb.b / 12.92;
rgb *= 100.0;
return vec3(rgb.r * 0.4124 + rgb.g * 0.3576 + rgb.b * 0.1805,
rgb.r * 0.2126 + rgb.g * 0.7152 + rgb.b * 0.0722,
rgb.r * 0.0193 + rgb.g * 0.1192 + rgb.b * 0.9505);
}
vec3 xyz2lab(vec3 xyz) {
xyz = xyz / vec3(94.811, 100.000, 107.304);
xyz = vec3( xyz.r > 0.008856 ? pow( xyz.r, 1.0/3.0) : (7.787 * xyz.r) + (16.0 / 116.0),
xyz.g > 0.008856 ? pow( xyz.g, 1.0/3.0) : (7.787 * xyz.g) + (16.0 / 116.0),
xyz.b > 0.008856 ? pow( xyz.b, 1.0/3.0) : (7.787 * xyz.b) + (16.0 / 116.0));
return vec3( (116.0 * xyz.y) - 16.0, 500.0 * (xyz.x - xyz.y), 200.0 * (xyz.y - xyz.z) );
}
vec3 rgb2lab(in vec3 rgb) {
vec3 xyz = rgb2xyz(rgb);
vec3 lab = xyz2lab(xyz);
return(lab);
}
float colorDifferenceCIE94FromLab(vec3 cieLab1, vec3 cieLab2) {
// Just to make it more readable
float cL1 = cieLab1.r;
float ca1 = cieLab1.g;
float cb1 = cieLab1.b;
float cL2 = cieLab2.r;
float ca2 = cieLab2.g;
float cb2 = cieLab2.b;
float c1 = sqrt(ca1 * ca1 + cb1 * cb1);
float c2 = sqrt(ca2 * ca2 + cb2 * cb2);
float dL = cL2 - cL1;
float dC = c2 - c1;
float dE = sqrt( (cL1 - cL2) * (cL1 - cL2) + (ca1 - ca2) * (ca1 - ca2) + (cb1 - cb2) * (cb1 - cb2) );
float dH = (dE * dE) - (dL * dL) - (dC * dC);
dH = dH > 0.0 ? sqrt(dH) : 0.0;
float kL = 1.0;
float kC = 1.0;
float kH = 1.0;
float k1 = 0.045;
float k2 = 0.015;
float sL = 1.0;
float sC = 1.0 + ( k1 * c1 ); // sX
float sH = 1.0 + ( k2 * c1 ); // sH
float dLw = dL / (kL * sL);
float dCw = dC / (kC * sC);
float dHw = dH / (kH * sH);
float deltaE94 = sqrt(dLw * dLw + dCw * dCw + dHw * dHw);
return deltaE94;
}
float colorDifferenceCIE94FromRGB(vec3 rgb1, vec3 rgb2) {
vec3 lab1 = rgb2lab(rgb1);
vec3 lab2 = rgb2lab(rgb2);
return colorDifferenceCIE94FromLab(lab1, lab2);
}
// float colorDifferenceCIE94FromRGB(vec3 rgb1, vec3 rgb2) {
// return abs(rgb2.g - rgb1.g);
// }
void main()
{
vec2 uv = gl_FragCoord.xy / screenResolution.xy;
vec3 pixel = texture2D(textureSampler, uv).xyz;
vec3 white = vec3(1.0);
vec3 black = vec3(0.0);
vec3 c1 = rotateHue(vec4(1.0, 0.0, 0.0, 1.0), hueRotation).xyz;
vec3 c2 = rotateHue(vec4(0.0, 1.0, 0.0, 1.0), hueRotation).xyz;
vec3 c3 = rotateHue(vec4(0.0, 0.0, 1.0, 1.0), hueRotation).xyz;
/*
vec3 c1 = vec3(1.0, 0.0, 0.0);
vec3 c2 = vec3(0.0, 1.0, 0.0);
vec3 c3 = vec3(0.0, 0.0, 1.0);
*/
vec3 c12 = mixColors(c1, c2);
vec3 c13 = mixColors(c1, c3);
vec3 c23 = mixColors(c2, c3);
vec3 c123 = mixColors(c1, c2, c3);
colors[0] = white;
colors[1] = black;
colors[2] = c1;
colors[3] = c2;
colors[4] = c3;
colors[5] = c12;
colors[6] = c13;
colors[7] = c23;
colors[8] = c123;
// colors[8] = vec3(0.8, 0.4, 0.1);
/*
colors[0] = white;
colors[1] = black;
colors[2] = vec3(1.0, 0.0, 0.8);
colors[3] = vec3(0.0, 0.7, 0.4);
colors[4] = vec3(0.0, 0.8, 0.9);
colors[5] = vec3(0.8, 0.4, 0.1);
colors[6] = vec3(0.4, 0.9, 0.0);
colors[7] = vec3(0.1, 0.2, 7.0);
colors[8] = vec3(0.9, 0.1, 0.0);
*/
float minDistance = 1000000.0;
vec3 finalColor1 = vec3(0.0);
int index1 = 0;
float distance = colorDifferenceCIE94FromRGB(pixel, colors[0]);
if(distance < minDistance) {
finalColor1 = colors[0];
minDistance = distance;
index1 = 0;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[1]);
if(distance < minDistance) {
finalColor1 = colors[1];
minDistance = distance;
index1 = 1;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[2]);
if(distance < minDistance) {
finalColor1 = colors[2];
minDistance = distance;
index1 = 2;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[3]);
if(distance < minDistance) {
finalColor1 = colors[3];
minDistance = distance;
index1 = 3;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[4]);
if(distance < minDistance) {
finalColor1 = colors[4];
minDistance = distance;
index1 = 4;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[5]);
if(distance < minDistance) {
finalColor1 = colors[5];
minDistance = distance;
index1 = 5;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[6]);
if(distance < minDistance) {
finalColor1 = colors[6];
minDistance = distance;
index1 = 6;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[7]);
if(distance < minDistance) {
finalColor1 = colors[7];
minDistance = distance;
index1 = 7;
}
distance = colorDifferenceCIE94FromRGB(pixel, colors[8]);
if(distance < minDistance) {
finalColor1 = colors[8];
minDistance = distance;
index1 = 8;
}
int index2 = 0;
vec3 finalColor2 = pixel;
minDistance = 100000.0;
for(int i=0 ; i<9 ; i++) {
distance = colorDifferenceCIE94FromRGB(pixel, colors[i]);
if(distance < minDistance) {
finalColor2 = colors[i];
minDistance = distance;
index2 = i;
}
}
vec3 green = vec3(0.0, 1.0, 0.0);
vec3 red = vec3(1.0, 0.0, 0.0);
/*
// Display colors:
if(uv.x < 0.1) {
float y = uv.y;
finalColor1 = colors[int(floor(y * 8.9))];
}
*/
// gl_FragColor = vec4(index1 == index2 ? green : red,1.0);
gl_FragColor = vec4((uv.x < 0.5 ? finalColor1 : finalColor2), 1.0);
}
</script>
<script type="application/glsl" id="vertexShader">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0 );
}
</script>
<!-- <script src="src/index.js"></script> -->
<script type="application/javascript">
//import * as vertexShader from "./vertex-shader";
//import * as fragmentShader from "./fragment-shader";
let fragmentShader = document.getElementById("fragmentShader").textContent;
let vertexShader = document.getElementById("vertexShader").textContent;
let screenWidth = window.innerWidth;
let screenHeight = window.innerHeight;
// let screenWidth = document.body.clientWidth;
// let screenHeight = document.body.clientHeight;
let uniforms = {};
let scene = null;
let parameters = {
hueRotation: 0
};
let renderer, camera, texture, raster;
var paperCanvas = document.getElementById("paper");
paper.setup(paperCanvas);
function initialize() {
let canvas = $("#three").get(0);
let context = canvas.getContext("webgl2");
renderer = new THREE.WebGLRenderer({
context: context,
canvas: canvas,
antialias: true,
preserveDrawingBuffer: true
});
renderer.setSize(screenWidth, screenHeight);
scene = new THREE.Scene();
camera = new THREE.OrthographicCamera(
screenWidth / -2,
screenWidth / 2,
screenHeight / 2,
screenHeight / -2,
1,
1000
);
texture = new THREE.CanvasTexture(paper.view.element, THREE.UVMapping);
texture.needsUpdate = true;
window.texture = texture;
uniforms = {
screenResolution: {
type: "v2",
value: new THREE.Vector2(screenWidth, screenHeight)
},
textureSampler: { value: texture },
textureResolution: new THREE.Uniform(
new THREE.Vector2(
canvas ? canvas.height / 2 : 0,
canvas ? canvas.width / 2 : 0
)
),
hueRotation: { type: "f", value: parameters.hueRotation }
};
let material = new THREE.ShaderMaterial({
uniforms: uniforms,
// extensions: { derivatives: true },
vertexShader: vertexShader.trim(),
fragmentShader: fragmentShader.trim(),
side: THREE.DoubleSide
});
let mesh = new THREE.Mesh(
new THREE.PlaneGeometry(screenWidth, screenHeight),
material
);
// mesh = new THREE.Mesh(geometry, material);
mesh.position.z = -1;
window.mesh = mesh;
scene.add(mesh);
//raster = new paper.Raster("./Velo.jpg");
//raster.onLoad = rasterLoaded;
let circle = new paper.Path.Circle(paper.view.bounds.center, 25);
circle.fillColor = "red";
setTimeout(() => { texture.needsUpdate = true }, 100);
}
$(document).ready(()=> {
let paperCanvas = $("#paper").get(0);
screenWidth = paperCanvas.clientWidth;
screenHeight = paperCanvas.clientHeight;
initialize();
})
//document.addEventListener("DOMContentLoaded", initialize);
function rasterLoaded() {
console.log("raster loaded");
// raster.fitBounds(paper.view.bounds);
console.log("texture:", texture);
if (texture) {
console.log("texture.needsUpdate");
texture.needsUpdate = true;
}
setTimeout(() => updateUniforms(parameters), 100);
}
function updateUniforms(parameters) {
if (uniforms == null) {
return;
}
uniforms.hueRotation.value = parameters.hueRotation;
texture.needsUpdate = true;
}
function animate() {
requestAnimationFrame(animate);
if (renderer) {
renderer.render(scene, camera);
}
}
animate();
</script>
</body>
</html>