Outlining in Three.js

Can I achieve a black outline effect on my 3D models using three.js?

I'm looking to create graphics similar to Borderlands 2, with toon shading and distinct black outlines.

Answer №1

It seems like I arrived late. Hopefully, this information will be helpful for someone in the future.

Here's a tip: you don't have to render everything twice. By duplicating the mesh and setting the duplicate mesh's material side to "backside", you can avoid the overhead of double rendering. This way, you'll only be rendering two meshes with most of the outline's geometry culled by WebGL's "backface culling".

Here's an example to illustrate this concept:

var scene = new THREE.Scene();

//Create main object
var mesh_geo = new THREE.BoxGeometry(1, 1, 1);
var mesh_mat = new THREE.MeshBasicMaterial({color : 0xff0000});
var mesh = new THREE.Mesh(mesh_geo, mesh_mat);
scene.add(mesh);

//Create outline object
var outline_geo = new THREE.BoxGeometry(1, 1, 1);
//Adjust the material's side property
var outline_mat = new THREE.MeshBasicMaterial({color : 0x00ff00, side: THREE.BackSide});
var outline = new THREE.Mesh(outline_geo, outline_mat);
//Scale the object to create an outline
outline.scale.multiplyScalar(1.5);
scene.add(outline);

If you want more information on backface culling, you can visit: http://en.wikipedia.org/wiki/Back-face_culling

This approach is effective for adding outlines to objects without resorting to toon shading and sacrificing realism.

Toon shading itself supports edge detection. The 'cel' shader in Borderlands was developed for this purpose.

In cel shading, developers can choose between object duplication (usually at a low pipeline level) or using image processing filters for edge detection. This decision involves a performance tradeoff between the two techniques.

For more information on cel shading, you can visit: http://en.wikipedia.org/wiki/Cel_shading

Answer №2

Yes, achieving toon shading in three.js is possible, but it's not a straightforward process. You can find shaders for toon shading in /examples/js/ShaderToon.js.

One common method for creating outlines in three.js is to render in two passes. The first pass renders the models in black at a slightly larger size, while the second pass uses the toon shaders at normal scale. This creates a larger black outline around the models. While not perfect, this method is commonly used for creating outlines in three.js. Another approach you can try is searching for "three.js hidden line rendering" as it uses a similar method to achieve a different look.

Answer №3

Although this question is from a while ago, I'll share what I did in response.

For my CG course, I developed an Outlined Cel-shader. Currently, it requires 3 rendering passes, but I am working on streamlining it to eliminate one pass.

Here's my approach: 1) Generate a NormalDepth image and save it to a texture.

In the vertex shader, convert the position to screen space and the normal to screen space.

In the fragment shader, calculate the depth of the pixel and generate the normal color using the depth as the alpha value.

float ndcDepth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) / (gl_DepthRange.far - gl_DepthRange.near);
float clipDepth = ndcDepth / gl_FragCoord.w;

2) Render the scene onto a texture with cel-shading. I modified the scene override material for this step.

3) Create a quad and render both textures onto it. Use an orthographic camera to view the quad. The cel-shaded texture is directly rendered on the quad, while the normaldepth shaded texture undergoes edge detection to determine when a pixel should be black (indicating an edge).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the significance of having nodejs installed in order to run typescript?

What is the reason behind needing Node.js installed before installing TypeScript if we transpile typescript into JavaScript using tsc and run our code in the browser, not locally? ...

Navigating through the elements of the DOM

Here is a function that deletes people from a list when you click on a certain part of the form: function ParticipantsDeleteClick(model, url) { for (i in model.Participants) { $("#delete" + i).click(function () { $.ajax({ url: url, ...

Unable to interact with buttons located in the title bar of the Electron application

I am currently working on developing a basic Text Editor using Electron. I am facing an issue with adding a custom title bar where the buttons are not clickable. To try and fix this issue, I have included an onclick tag to the buttons in my code. main.js ...

I am encountering a "TypeError: topics.forEach is not a function" error when attempting to retrieve metadata for topics using my kafkajs client in Node.js/express.js. Can anyone help me understand why

I am attempting to retrieve the metadata of my Kafka brokers' topics using the kafkajs admin client within my Node.js + express.js server. Here is the content of my index.js file, serving as the main entrypoint for npm: 'use strict'; cons ...

When executed through nodeJS using the `require('./main.ts')` command, TypeScript Express encountered an issue with exporting and displayed undefined

Describing my issue is proving to be challenging, so I have simplified the code. Let me share the code below: main.ts import express from 'express'; let a = 1 console.log ('a in main.ts', a) export let b = a const app = express() let ...

hyperlink to choose a specific option from a drop-down menu

The challenge I am currently working on involves page1.html <a href="/foo"></a> foo.html <select ng-model="ctrl.listvalues"> <option id="{{item.value}}" ng-repeat="item" in ctrl.availableValues" value="{{item.value}}">item.di ...

"Create dynamic tables with AngularJS using ng-repeat for column-specific rendering

I have a model called Item with the following structure: {data: String, schedule: DateTime, category: String} I want to create a report that displays the data in a table format like this: <table> <tr> <th>Time Range</th&g ...

Experience the power of ReactJS as you utilize the componentDidMount lifecycle method to fetch data

Currently, I am in the process of learning how to utilize ReactJS, Spotify API, and Promises. My goal is to retrieve top albums from musicians on Spotify and play a 30-second snippet of their tracks. I have decided to work with a Spotify package known as ...

Attempting to generate printed documents with multiple components spread across individual pages using react-to-print

I am working with the react-to-print library and I have a requirement to print a list of components, with each component on its own separate page. However, when I click on the print button, I encounter an error stating that the argument does not appear to ...

Attempting to incorporate icons into a Material UI table design

Hello, I've incorporated a Material UI table into one of my projects with a design concept similar to this - https://i.stack.imgur.com/i6Fsj.png I'm aiming to include icons within the tables. Here's the code I've worked on so far - ...

Launching an embedded webpage in a separate tab within the main browser window

In my current setup, I have implemented an iframe within the main window. The iframe includes a form where users can input data and submit it. Currently, I achieve the submission with the following code: var openURL = '/results/finalpage'; windo ...

Utilize a while loop in JavaScript to trigger a message when a variable dips below zero

Forgive me if I seem clueless. I am a beginner in the world of Javascript and am currently experimenting with loops. At the moment, I am toying around with this particular piece of code: <!DOCTYPE html> <html> <body> <button oncl ...

Regular expression for textarea validation

I'm currently working on creating a regex for a textarea in my Angular 8 application. The goal is to allow all characters but not permit an empty character at the start. I've experimented with 3 different regex patterns, each presenting its own s ...

How to Develop a Custom getText() Function for a Selenium Element?

Creating a custom Selenium getText() method that can retrieve either the node text or the node plus child text of an element is my current challenge. The default behavior of Selenium appears to utilize the Xpath .//string() method which includes the text o ...

Locate a JQuery element within another JQuery element

Apologies for my poor grasp of English. I am working with HTML and JavaScript. <noindex> <h1>This is h1 in noindex</h1> <div> <h1>This is h1 in noindex and in div</h1> <div> <h1>This is h1 in noindex a ...

Creating a triangle number pattern in JavaScript with a loop

Hi there, I'm currently facing an issue. I am trying to generate a triangular number pattern like the one shown below: Output: 1223334444333221 =22333444433322= ===3334444333=== ======4444====== I attempted to write a program for this, however, ...

Is there a way to retrieve the central anchor point position (x, y) of the user's selection in relation to the document or window?

Is there a way to retrieve the center anchor point position (x, y) of the user's selection relative to the document or window? I have tried using window.getSelection() to get selected nodes, but I am unsure how to obtain their position: See an examp ...

Add a CSS class to a different element when hovering

I have the following HTML structure: <div class="container"> <div class="list-wrapper"> <ul> <li class="item-first"> </li> </ul> </div> <div class="description-wrapper"> <d ...

How to use hooks in NETXJS to pass data from a page to a component

Hey there, how are you doing? I am currently working on a project using Next.js and styled components. I am trying to change a string using props in my layout component, where the main content is dynamic per page. Page 'prueba.js' import React f ...

Looping through an array and appending distinct elements to a fresh array

I am currently facing an issue and seeking feedback on how to resolve it. Below is the JSON data: questions: [ { question: 'lala', answer: 'papa', categories: ['Handla'] }, { question: ...