threejs中创建3D调色板

请添加图片描述
上面gif效果不是太好,是因为录屏软件问题,下面的截图是正常效果
请添加图片描述

代码如下:

import {
    PlaneBufferGeometry,
    ShaderMaterial,
    Vector4,
    DoubleSide,
    Mesh,
    Object3D,
    Color,
    Vector2
} from 'three';


/**
 * 创建调色板
 * 使用方式
 * 1、引入库
 *     import { ColorPicker } from './ColorPicker.js';
 * 2、创建对象并添加到场景 
 *      this.colorPicker = new ColorPicker();
        this.scene.add(this.colorPicker.colorPicker);
 * 3、使用射线检测来修改选中颜色
        var planeIntersects = this.ray.intersectObjects(this.scene.children, true);
        var planeIntersect =  planeIntersects== null ? null : planeIntersects[0];

        if (planeIntersect) {
            if(this.colorPicker && planeIntersect.object.name.indexOf("ColorPicker")>=0){
                let newColor = this.colorPicker.getColor(planeIntersect);
                console.log(newColor);
            }
        }
 */
class ColorPicker {
    constructor() {
        this.saturationParams = {
            segment:1,
            targetColor:new Vector4(1,0,0,1),
            maskColor:new Vector4(1,1,1,1),
            maskOffset:0.015,
            maskPos:new Vector2(0,0)
        }

        this.hueParams = {
            segment:1.0,
            width:0.1,
            height:1.0,
            hues:[2,0,1,2,0,1],
            colors:[new Vector4(1, 0, 0, 1), new Vector4(0, 0, 1, 1), new Vector4(0, 0, 1, 1), new Vector4(0, 1, 0, 1), new Vector4(0, 1, 0, 1), new Vector4(1, 0, 0, 1)],
            maskColor:new Vector4(1,1,1,1),
            maskOffset:0.01,
        }

        this.colorPicker = null;
        this.CreateColorPicker();
    }

    /**
     * 创建调色板
     */
    CreateColorPicker() {
        this.colorPicker = new Object3D();
        this.colorPicker.name = "ColorPicker";

        let Saturation = this.createSaturation();
        this.colorPicker.attach(Saturation);
        this.colorPicker._Saturation = Saturation;

        let Hue = this.createHue();
        this.colorPicker.attach(Hue);
        this.colorPicker._Hue = Hue;
    }

    /**
     * 获取点击颜色
     * @param {*} intersect 射线检测到的物体
     * @returns 
     */
    getColor(intersect){
        if(intersect && intersect.object.name.indexOf("ColorPicker")>=0){
            let obj = intersect.object;
            if(obj._type && obj._type == "Hue"){
                this.changeHue(intersect.point);
                return this.selectColor(this.saturationParams.maskPos);
            }
            else if(obj._type && obj._type == "Saturation"){
                intersect.point.y *= -1;
                return this.selectColor(intersect.point);
            }
        }
    }

    changeHue(point) {
        this.colorPicker._Hue.material.uniforms.maskHeight.value = point.y;

        let y = (point.y + this.hueParams.height / 2) / this.hueParams.height;
        let color = this.getHueColor(y);

        this.colorPicker._Saturation.material.uniforms.targetColor.value = color;
        this.saturationParams.targetColor = color;
    }

    selectColor(point){
        this.colorPicker._Saturation.material.uniforms.maskPos.value = new Vector2(point.x,point.y);
        this.saturationParams.maskPos = new Vector2(point.x,point.y)
        var x = (point.x+this.saturationParams.segment/2)/this.saturationParams.segment;
        var y = (point.y+this.saturationParams.segment/2)/this.saturationParams.segment;
        return this.getSaturationColor([this.saturationParams.targetColor.x,this.saturationParams.targetColor.y,this.saturationParams.targetColor.z],x,y);
    }

    getSaturationColor(color,x,y){
        var newColor = [1,1,1];
        for (var i = 0; i < 3; i++)
        {
            if (color[i] != 1)
            {
                newColor[i] = (1 - color[i]) * (1 - x) + color[i];
            }
        }

        for (var i = 0; i < 3; i++)
        {
            newColor[i] *= (1 - y);
        }

        return new Color(newColor[0],newColor[1],newColor[2],1);
    }

    /**
     * 根据y值获取hue选中的颜色
     * @param {*} y 
     * @returns 
     */
    getHueColor(y){
        if(y<0) y=0.0;
        if(y>1.0) y=1.0;
        var c = 0.166667;
        var index = Math.floor(y / c);
        var h = this.hueParams.hues[index];
        var newColor = this.hueParams.colors[index].clone();
        var less = (y - index * c) / c;
        var value = index % 2 == 0 ? less : 1.0 - less;
        if(h == 0) newColor.x = value;
        else if(h == 1) newColor.y = value;
        else if(h == 2) newColor.z = value;
        return newColor;
    }

    /**
     * 创建Saturation
     * @returns 
     */
    createSaturation() {
        let segment = 1;
        let planeGeo = new PlaneBufferGeometry(this.saturationParams.segment, this.saturationParams.segment);
        let planeMat = new ShaderMaterial({
            uniforms: {
                targetColor: { value: this.saturationParams.targetColor},
                resolution: { value: this.saturationParams.segment },
                maskPos:{value:this.saturationParams.maskPos},
                maskColor:{value:this.saturationParams.maskColor},
                maskOffset:{value:this.saturationParams.maskOffset},
            },
            side: DoubleSide,
            vertexShader: [
                "varying vec3 modelPos;",
                "void main() {",
                " modelPos = position;",
                "	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
                "}"
            ].join("\n"),
            fragmentShader: [
                "uniform vec4 targetColor;",
                "uniform float resolution;",
                "uniform float maskOffset;",
                "uniform vec4 maskColor;",
                "uniform vec2 maskPos;",
                "varying vec3 modelPos;",
                "vec4 GetSaturation(vec4 targetC, float x, float y)",
                "{",
                " vec4 newColor = vec4(1.0,1.0,1.0,1.0);",
                " for (int i = 0; i < 3; i++)",
                " {",
                "   if (targetC[i] < 0.999)",
                "   {",
                "     newColor[i] = (1.0 - targetC[i]) * (1.0 - x) + targetC[i];",
                "   }",
                " }",

                " newColor *= (1.0 - y);",
                " newColor.a = 1.0;",
                " return newColor;",
                "}",

                "void main() {",
                "   if(modelPos.x >= maskPos.x-maskOffset && modelPos.x <= maskPos.x+maskOffset && modelPos.y >= maskPos.y-maskOffset && modelPos.y <= maskPos.y+maskOffset)",
                "       gl_FragColor = maskColor;",
                "   else{",
                "       float x = (modelPos.x + resolution/2.0)/resolution;",
                "       float y = (modelPos.y + resolution/2.0)/resolution;",
                "       gl_FragColor = GetSaturation(targetColor,x,y);",
                "   }",
                "}"
            ].join("\n")
        });
        let plane = new Mesh(planeGeo, planeMat);
        plane.rotateX(Math.PI);
        plane.scale.set(1 / segment, 1 / segment, 1 / segment);
        plane._type = "Saturation";
        plane.name = "ColorPicker_Saturation";
        return plane;
    }

    /**
     * 创建hue
     * @returns 
     */
    createHue() {
        let planeGeo = new PlaneBufferGeometry(this.hueParams.width, this.hueParams.height, this.hueParams.segment, this.hueParams.segment);
        let planeMat = new ShaderMaterial({
            uniforms: {
                resolution: { value: this.hueParams.height },
                hues: { value: this.hueParams.hues },
                maskHeight: { value: this.hueParams.height / 2.0 },
                colors: { value: this.hueParams.colors },
                maskColor: { value: this.hueParams.maskColor },
                maskOffset: { value: this.hueParams.maskOffset },
            },
            side: DoubleSide,
            vertexShader: [
                "varying vec3 modelPos;",
                "void main() {",
                " modelPos = position;",
                "	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
                "}"
            ].join("\n"),
            fragmentShader: [
                "uniform float resolution;",
                "uniform float maskHeight;",
                "uniform int[6] hues;",
                "uniform vec4[6] colors;",
                "varying vec3 modelPos;",
                "float c = 0.166667;",
                "uniform float maskOffset;",
                "uniform vec4 maskColor;",
                "vec4 GetHue(float y)",
                "{",
                "   y = clamp(y,0.0,1.0);",
                "   highp int index = int(floor(y/c));",
                "   int h = hues[index];",
                "   vec4 newColor = colors[index];",
                "   float less = (y-float(index)*c)/c;",
                "   newColor[h] = index % 2 == 0 ? less : 1.0-less;",
                "   return newColor;",
                "}",

                "void main() {",
                "   if(modelPos.y > maskHeight - maskOffset && modelPos.y<maskHeight + maskOffset)",
                "       gl_FragColor = maskColor;",
                "   else{",
                "       float y = (modelPos.y + resolution/2.0)/resolution;",
                "       gl_FragColor = GetHue(y);",
                "   }",
                "}"
            ].join("\n")
        });
        let plane = new Mesh(planeGeo, planeMat);
        plane.rotateX(Math.PI);
        plane.rotateZ(Math.PI);
        plane.scale.set(1 / this.hueParams.segment, 1 / this.hueParams.segment, 1 / this.hueParams.segment);
        plane.position.x += 0.6;
        plane._type = "Hue";
        plane.name = "ColorPicker_Hue";
        return plane;
    }
}

export { ColorPicker }

参考:用 Unity 实现调色板功能

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值