suirunzhao / AI-Art-Gallery

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CL Final Project: AI-Art-Gallery

cover

Link to the project:

https://ai-art-gallery.glitch.me

Link to the Slide:

https://docs.google.com/presentation/d/1hxi4xvh_hBIOo8SXlfsuKn6TbpRaSL0V/edit?usp=sharing&ouid=101571181520503869864&rtpof=true&sd=true

Background:

If you've been on social media throughout the last few months, chances are you've seen a plethora of artwork, written content, memes and more - created not by a human, but by an AI. Whilst software and new machine-learning AI systems such as DALL-E 2 and Midjourney have recently become the latest creative craze online, there has been little discussion as to the legal questions surrounding AI-generated content. It may seem a bit surreal or dystopian for some, but there is now a whole new universe of questions to be raised regarding intellectual property and AI.

As much machine-learning software utilises a database of images collated from other artists. Is it stealing, copying, or inspiration if you utilize someone else's artwork and artificial intelligence to create your own? The largest issue still is: Who owns AI-generated art once it has been produced?

My initial critical thinking about AI-Generated art is about the copyright, is it belongs to the artist(user)/developer/company/AI? In other words, what do people or organizations think about AI-generated art? Do arts must have to be created by people?

Concept Write-Up:

Combining Three.js, Socket.IO, MongoDB to create a simple AI-Generated art gallery website to invite people who haven’t used AI-generated art before in order to raise people’s awareness of AI copyright thinking.

Production decisions (i.e. technical, design, creative, etc.)

  1. Create the cover page background using Three.js, users can move their mouse to control the point light. Using Socket.IO to allow multiple users to join in and create more point lights.
  2. The gallery section is the place to store the image data people uploaded(MongoDB).
  3. Popup window will show a warning and copyright message which is randomly generated by the algorithm when users click the upload button.

Major challenges and solutions:

1. To create the gallery space to store the image and fit the different size of window, I use css to build the section.
#gallery {
    line-height:0;
    -webkit-column-count:4; /* split it into 5 columns */
    -webkit-column-gap:16px; /* give it a 5px gap between columns */
    -moz-column-count:4;
    -moz-column-gap:16px;
    column-count:4;
    column-gap:16px;
 }

 #gallery img {
    width: 100%;
    height: auto;
    margin-bottom:5px; /* to match column gap */
    filter: grayscale(100%);
    transition: filter 1s;
 }

 #gallery img:hover {
    filter:none;
 }

 @media (max-width: 1200px) {
    #gallery {
     -moz-column-count:    3;
     -webkit-column-count: 3;
     column-count:         3;
    }
 }

 @media (max-width: 1000px) {
    #gallery {
     -moz-column-count:    2;
     -webkit-column-count: 2;
     column-count:         2;
    }
 }

 @media (max-width: 800px) {
    #gallery {
     -moz-column-count:    1;
     -webkit-column-count: 1;
     column-count:         1;
    }
 }

 @media (max-width: 400px) {
    #gallery {
     -moz-column-count:    1;
     -webkit-column-count: 1;
     column-count:         1;
    }
 }
  1. Use the mouse to control the point light position in Three.js. I created a function at the first time, but found it couldn't work when I tried to use Socket.IO to send data to the server, so I create a class to not only contains mouseMove function but also create a new random color point light as well. (Special thanks to Brian)
    class userLight {
        constructor(_scene, _camera) {
            this.scene = _scene
            this.position = new THREE.Vector3()
            this.mouse = new THREE.Vector2()
            this.camera = _camera
            this.pointLight = new THREE.PointLight(clientColor, 1, 5)
            this.pointLight.position.set(0, 0, 0);
            this.pointLight.castShadow = true;
            this.pointLight.shadow.bias = 0.0001;
            this.pointLight.mapSizeWidth = 1024; // Shadow Quality
            this.pointLight.mapSizeHeight = 1024; // Shadow Quality
            this.trigger = true
        }

        create() {
            if (this.trigger) {
                //this.mesh.add(new THREE.Mesh(this.geometry, this.material))
                this.scene.add(this.pointLight)
            }
            this.trigger = false
        }
        move(_position) {
            let vector = new THREE.Vector3(_position.x, _position.y, 0.5)
            vector.unproject(this.camera)
            let dir = vector.sub(this.camera.position).normalize()
            let distance = -this.camera.position.z / dir.z
            let pos = this.camera.position.clone().add(dir.multiplyScalar(distance))
            this.pointLight.position.copy(pos)
        }
    }
  1. Add a point light once a user connects to the server, and remove light when they disconnect. I add the ID to every specific user, and emit data when io.on
    let users = [];
    
    socket.on('dataAll', function (data) {
        for (let i = 0; i < users.length; i++) {
            if (data.id == users[i].id) {
                users[i].object.move(data.pos)
            }
        }
    })

    socket.on('user', function (data) {
        let u = new userLight(scene, camera)
        let user = {
            id: data,
            object: u,
        }
        users.push(user)
        for (let i = 0; i < users.length; i++) {
            users[i].object.create()
        }
    })

    socket.on('userLeft', function (id) {
        for (let i = 0; i < users.length; i++) {
            if (id == users[i].id) {
                scene.remove(users[i].object.mesh)
            }
        }
    })

The Socket.IO code in index.js file is like this

io.on('connection', function(socket) {
    console.log("We have a new client: " + socket.id);

    io.emit('user', socket.id)
    //Listen for a message named 'msg' from this client
    socket.on('data', function(data) {
        //Data can be numbers, strings, objects
        console.log("Received a 'data' event");
        console.log(data);

        //add id to user
        let obj = {
			pos: data,
			id: socket.id,
		}

    });

    //Listen for this client to disconnect
    socket.on('disconnect', function() {
        console.log("A client has disconnected: " + socket.id);
        io.emit('userLeft', socket.id)
    });
});
  1. Use gsap to create the animation when the model is successfully loaded.
        let objMaterial = new THREE.MeshPhongMaterial({ // Required For Shadows
            color: 0x888888,
            specular: 0x000000,
            shininess: 60,
        });

        //obj
        let objLoader = new THREE.OBJLoader();
        //new group
        let group = new THREE.Group();
        //objLoader.setMaterials(cubeMaterial);
        objLoader.setPath('assets/');
        objLoader.load('OR.obj', function (object) {
            object.traverse(function (child) {
                if (child instanceof THREE.Mesh) {
                    child.material = objMaterial;
                }
            });
            object.scale.set(0, 0, 0);
            object.rotation.set(Math.PI / 3, Math.PI / 3, Math.PI / 3);
            group.add(object);
            object.position.y = -13;
            scene.add(group);

            doAnimation();

            function doAnimation() {
                gsap.to(object.rotation, {
                    x: 0,
                    y: 0,
                    z: 0,
                    duration: 1, 
                    paused: true
                }).play()
                gsap.to(object.scale, {
                    x: 1,
                    y: 1,
                    z: 1,
                    duration: 3, 
                    paused: true
                }).play()
            }
        });
  1. In Upload section, the upload button will be disabled unless users select file from their computer, and popup window will then show up when people click the upload button.
    inputFile.addEventListener('change', function () {
        let image = this.files[0]
        if (image.size < 2000000) {
            let reader = new FileReader();
            upload.disabled = false;
            reader.onload = () => {
                let allImg = imgArea.querySelectorAll('img');
                allImg.forEach(item => item.remove());
                let imgUrl = reader.result;
                let img = document.createElement('img');
                img.src = imgUrl;
                imgArea.appendChild(img);
                imgArea.classList.add('active');
                imgArea.dataset.img = image.name;
            };
            reader.readAsDataURL(image);



        } else {
            alert("Image size more than 2MB");
        }
    })

    upload.addEventListener('click', function () {
        document.querySelector('h2').remove();
        upload.disabled = true;
        document.getElementById("popup").classList.toggle("active");
        //console.log("2")
        imgArea.querySelectorAll('img').forEach(item => item.remove());
        let word = ['You', 'Developers', 'Source Artists', 'AI', 'Software Companies'];
        let copyright = word[Math.floor(Math.random() * word.length)];
        //alert('Copyright' + copyright);
        let cp = document.createElement('h2');
        cp.innerHTML = "Copyright " + "Ⓒ " + copyright;
        newCp.appendChild(cp);
        //console.log(cp);
    });
  1. Found MongoDB can’t deploy on glitch, so I deleted all the database code:) And also because I couldn't convert image to json data.

Next Steps

  1. Store image in MongoDB, maybe will use Google Drive to store the image and generate link and use MongoDB to store the drive link.

  2. Rotate camera/3D models position to have a different perspective when users go into other section.

About


Languages

Language:JavaScript 73.2%Language:HTML 14.2%Language:CSS 12.6%