Skip to content

网格

mesh 描述了模型的几何信息(geometry)和材质信息(material)。如果你想用一个 Object3D 对象展示一个3D的模型,那么它必须添加 MeshRenderer 组件,它主要包含两个属性:

  1. geometry 对象,它决定了物体具体的几何形状,包括顶点位置拓扑UV等;
  2. material 对象,它决定了物体呈现的材质球样式,包括贴图颜色透明度等属性。

Orillusion 内置了几种常见的几何体,可以很方便的创建不同的 mesh类型:

长方体

BoxGeometry 类提供长方体创建功能。 参数概览:

参数描述
widthX轴上面的宽度,默认值为1。
heighty轴上面的高度,默认值为1。
depthZ轴上面的深度,默认值为1。

使用示例:

ts
import {Object3D, MeshRenderer, BoxGeometry} from '@orillusion/core';

let obj = new Object3D();
// 添加 MeshRenderer 组件
let mr = obj.addComponent(MeshRenderer);
// 设置组件 geometry
mr.geometry = new BoxGeometry(5,2,3);

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, LitMaterial, BoxGeometry, MeshRenderer, DirectLight, HoverCameraController, Color, Vector3, AtmosphericComponent, View3D } from '@orillusion/core';
await Engine3D.init();
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(0, 0, 15);
scene3D.addChild(cameraObj);

let light: Object3D = new Object3D();
let component: DirectLight = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1.0, 1.0, 1.0, 1.0);
component.intensity = 1;
scene3D.addChild(light);

// create a object
const obj: Object3D = new Object3D();
// add MeshRenderer to the object
let mr: MeshRenderer = obj.addComponent(MeshRenderer);
// set a box geometry
mr.geometry = new BoxGeometry(5, 2, 3);
// set a pbr lit material
mr.material = new LitMaterial();
// set location and rotation
obj.localPosition = new Vector3(0, 0, 0);
obj.localRotation = new Vector3(0, 45, 0);
scene3D.addChild(obj);

// add an Atmospheric sky enviroment
scene3D.addComponent(AtmosphericComponent).sunY = 0.6;
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

球体

SphereGeometry 类提供球体创建功能。 参数概览:

参数描述
widthSegments球体水平分段数(沿着经线分段)
heightSegments球体垂直分段数(沿着纬线分段)
phiStart(可选)指定水平(经线)起始角度
phiLength(可选)指定水平(经线)扫描角度的大小球体赤道线的弧长
thetaStart(可选)指定垂直(纬线)起始角度
thetaLength(可选)指定垂直(纬线)扫描角度大小

使用示例:

ts
import {Object3D, MeshRenderer, SphereGeometry} from '@orillusion/core';

let obj = new Object3D();
// 添加 MeshRenderer 组件
let mr = obj.addComponent(MeshRenderer);
// 设置组件 geometry
mr.geometry = new SphereGeometry(2, 50, 50);

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, AtmosphericComponent, View3D, LitMaterial, SphereGeometry, MeshRenderer, DirectLight, HoverCameraController, Color, Vector3 } from '@orillusion/core';
await Engine3D.init();
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(0, 0, 15);
scene3D.addChild(cameraObj);

let light: Object3D = new Object3D();
let component: DirectLight = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1.0, 1.0, 1.0, 1.0);
component.intensity = 1;
scene3D.addChild(light);

// create a object
const obj: Object3D = new Object3D();
// add MeshRenderer to the object
let mr: MeshRenderer = obj.addComponent(MeshRenderer);
// set a box geometry
mr.geometry = new SphereGeometry(2, 50, 50);
// set a pbr lit material
mr.material = new LitMaterial();
// set location
obj.localPosition = new Vector3(0, 0, 0);
scene3D.addChild(obj);

// add an Atmospheric sky enviroment
scene3D.addComponent(AtmosphericComponent).sunY = 0.6;
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

圆柱体

CylinderGeometry 类提供圆柱体创建功能。

参数概览:

参数描述
radiusTop顶部半径,默认值为1
radiusBottom底部半径,默认值为1
height桶高度,默认值为1
radialSegments圆面细分数量,默认值为8
heightSegments桶壁细分数量,默认值为8
openEnded是否开放的桶(有上下底),默认值为false
thetaStart第一个分段的起始角度,默认值为0
thetaLength圆形扇区的中心角,默认值为Math.PI * 2

使用示例:

ts
import {Object3D, MeshRenderer, CylinderGeometry} from '@orillusion/core';

let obj = new Object3D();
// 添加 MeshRenderer 组件
let mr = obj.addComponent(MeshRenderer);
// 设置组件 geometry
mr.geometry = new CylinderGeometry(2, 2, 10);

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, AtmosphericComponent, View3D, LitMaterial, CylinderGeometry, MeshRenderer, DirectLight, HoverCameraController, Color, Vector3 } from '@orillusion/core';
await Engine3D.init();
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(0, -15, 10);
scene3D.addChild(cameraObj);

let light: Object3D = new Object3D();
let component: DirectLight = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1.0, 1.0, 1.0, 1.0);
component.intensity = 1;
scene3D.addChild(light);

// create a object
const obj: Object3D = new Object3D();
// add MeshRenderer to the object
let mr: MeshRenderer = obj.addComponent(MeshRenderer);
// set a cylinder geometry
mr.geometry = new CylinderGeometry(1, 1, 1, 8, 8, false, 0, 2 * Math.PI);
// set a pbr lit material for 3 sub-geometries
let material = new LitMaterial();
mr.materials = [material, material, material];
// set location and rotation
obj.localPosition = new Vector3(0, 0, 0);
scene3D.addChild(obj);

// add an Atmospheric sky enviroment
scene3D.addComponent(AtmosphericComponent).sunY = 0.6;
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

圆环

TorusGeometry 类提供圆柱体创建功能。

参数概览:

参数描述
radius圆环半径,默认值0.4
tube管道半径,默认值0.1
radialSegments圆环细分数量,默认值32
tubularSegments管道细分数量,默认值32

使用示例:

ts
import {Object3D, MeshRenderer, TorusGeometry} from '@orillusion/core';

let obj = new Object3D();
// 添加 MeshRenderer 组件
let mr = obj.addComponent(MeshRenderer);
// 设置组件 geometry
mr.geometry = new TorusGeometry(3, 1, 32, 32);

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, LitMaterial, BoxGeometry, MeshRenderer, DirectLight, HoverCameraController, Color, Vector3, AtmosphericComponent, View3D, TorusGeometry } from '@orillusion/core';
await Engine3D.init();
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(0, 0, 15);
scene3D.addChild(cameraObj);

let light: Object3D = new Object3D();
let component: DirectLight = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1.0, 1.0, 1.0, 1.0);
component.intensity = 1;
scene3D.addChild(light);

// create a object
const obj: Object3D = new Object3D();
// add MeshRenderer to the object
let mr: MeshRenderer = obj.addComponent(MeshRenderer);
// set a box geometry
mr.geometry = new TorusGeometry(3, 1, 32, 32);
// set a pbr lit material
mr.material = new LitMaterial();
// set location and rotation
obj.localPosition = new Vector3(0, 0, 0);
obj.localRotation = new Vector3(90, 0, 0);
scene3D.addChild(obj);

// add an Atmospheric sky enviroment
scene3D.addComponent(AtmosphericComponent).sunY = 0.6;
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

平面

PlaneGeometry 类提供平面创建功能。

参数概览:

参数描述
width平面沿着X轴的宽度。默认值是1。
height平面沿着Y轴的高度。默认值是1。
segmentW平面的宽度分段数,默认值是1。
segmentH平面的高度分段数,默认值是1。
up平面朝向,默认值是Vector3.Y_AXIS。

使用示例:

ts
import {Object3D, MeshRenderer, PlaneGeometry} from '@orillusion/core';

let obj = new Object3D();
// 添加 MeshRenderer 组件
let mr = obj.addComponent(MeshRenderer);
// 设置组件 geometry
mr.geometry = new PlaneGeometry(100, 100, 1, 1);

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, AtmosphericComponent, View3D, LitMaterial, PlaneGeometry, MeshRenderer, DirectLight, HoverCameraController, Color, Vector3, GPUCullMode } from '@orillusion/core';
await Engine3D.init();
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(0, -15, 80);
scene3D.addChild(cameraObj);

let light: Object3D = new Object3D();
let component: DirectLight = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1.0, 1.0, 1.0, 1.0);
component.intensity = 1;
scene3D.addChild(light);

// create a object
const obj: Object3D = new Object3D();
// add MeshRenderer to the object
let mr: MeshRenderer = obj.addComponent(MeshRenderer);
// set a plane geometry
mr.geometry = new PlaneGeometry(20, 20);
// set a pbr lit material
mr.material = new LitMaterial();
// render double side
mr.material.cullMode = GPUCullMode.none;
// set location and rotation
obj.localPosition = new Vector3(0, 0, 0);
obj.localRotation = new Vector3(0, 45, 0);
scene3D.addChild(obj);

// add an Atmospheric sky enviroment
scene3D.addComponent(AtmosphericComponent).sunY = 0.6;
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

挤压缓冲几何体

ExtrudeGeometry 类提供从一个形状路径中挤压出一个几何体的功能。

使用示例:

ts
import {Object3D, MeshRenderer, ExtrudeGeometry, Vector3} from '@orillusion/core';

let conduitObject3D = new Object3D();
// 添加 MeshRenderer 组件
let mr = conduitObject3D.addComponent(MeshRenderer);
// 创建自定义形状
let shape: Vector3[] = [], vertexCount = 8, shapeRadius = 1
for (let i = 0; i < vertexCount; i++) {
    let angle = Math.PI * 2 * i / vertexCount;
    let point = new Vector3(Math.sin(angle), 0, Math.cos(angle)).multiplyScalar(shapeRadius);
    shape.push(point);
}
// 创建自定义路径
let curve: Vector3[] = [], sectionCount = 60, modelRadius = 4
for (let i = 0; i < sectionCount; i++) {
    let angle = Math.PI * 2 * i / 20;
    modelRadius += 0.1 * i / sectionCount;
    let offsetY = 0.6 - Math.sqrt(i / sectionCount);
    let point = new Vector3(Math.sin(angle), offsetY * 6, Math.cos(angle)).multiplyScalar(modelRadius);
    curve.push(point);
}
// 创建 Extrude Geometry
mr.geometry = new ExtrudeGeometry().build(shape, true, curve, 0.2);

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, Scene3D, Object3D, Camera3D, LitMaterial, MeshRenderer, DirectLight, HoverCameraController, Color, Vector3, AtmosphericComponent, View3D, ExtrudeGeometry, BitmapTexture2D, GPUCullMode } from '@orillusion/core';

await Engine3D.init();
let scene3D: Scene3D = new Scene3D();
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(0, 0, 40);
scene3D.addChild(cameraObj);

let light: Object3D = new Object3D();
let component: DirectLight = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1.0, 1.0, 1.0, 1.0);
component.intensity = 1;
scene3D.addChild(light);

// create a object
const obj: Object3D = new Object3D();
// add MeshRenderer to the object
let mr: MeshRenderer = obj.addComponent(MeshRenderer);

// build shape
let shape: Vector3[] = [],
    vertexCount = 8,
    shapeRadius = 1;
for (let i = 0; i < vertexCount; i++) {
    let angle = (Math.PI * 2 * i) / vertexCount;
    let point = new Vector3(Math.sin(angle), 0, Math.cos(angle)).multiplyScalar(shapeRadius);
    shape.push(point);
}
// build curve path
let curve: Vector3[] = [],
    sectionCount = 60,
    modelRadius = 4;
for (let i = 0; i < sectionCount; i++) {
    let angle = (Math.PI * 2 * i) / 20;
    modelRadius += (0.1 * i) / sectionCount;
    let offsetY = 0.6 - Math.sqrt(i / sectionCount);
    let point = new Vector3(Math.sin(angle), offsetY * 6, Math.cos(angle)).multiplyScalar(modelRadius);
    curve.push(point);
}

// build ExtrudeGeometry from shape & curve
mr.geometry = new ExtrudeGeometry().build(shape, true, curve, 0.2);
// set a pbr lit material
mr.material = new LitMaterial();
let texture = new BitmapTexture2D();
texture.addressModeU = 'repeat';
texture.addressModeV = 'repeat';
await texture.load('https://cdn.orillusion.com/textures/grid.webp');
mr.material.baseMap = texture;
mr.material.cullMode = GPUCullMode.none;

scene3D.addChild(obj);

// add an Atmospheric sky enviroment
scene3D.addComponent(AtmosphericComponent).sunY = 0.6;
// create a view with target scene and camera
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
// start render
Engine3D.startRenderView(view);

自定义几何体

我们可以通过更新现有的几何体的顶点 vertexBuffer 来自定义几何体的形状

使用示例:

ts
import {Object3D, MeshRenderer, PlaneGeometry, LitMaterial, VertexAttributeName} from '@orillusion/core';

let obj = new Object3D();
// 添加 MeshRenderer 组件
let mr = obj.addComponent(MeshRenderer);
// 设置一个平面作为基础, 定义大小和分段数量
mr.geometry = new PlaneGeometry(100, 100, 100, 100);
mr.material = new LitMaterial()

// 获得现有顶点信息
let posAttrData = mr.geometry.getAttribute(VertexAttributeName.position);
// 重写所有顶点 xyz 坐标
for (let i = 0, count = posAttrData.data.length / 3; i < count; i++) {
    posAttrData.data[i * 3 + 0] = Math.random(); // position x
    posAttrData.data[i * 3 + 1] = Math.random(); // position y
    posAttrData.data[i * 3 + 2] = Math.random(); // poisiton z
}
// 更新顶点信息
mr.geometry.vertexBuffer.upload(VertexAttributeName.position, posAttrData);
// 重新计算法向量
mr.geometry.computeNormals();

我们甚至可以在主循环中每帧更改顶点信息

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { View3D, PlaneGeometry, Engine3D, Scene3D, AtmosphericComponent, CameraUtil, HoverCameraController, Object3D, DirectLight, KelvinUtil, MeshRenderer, LitMaterial, VertexAttributeName, Time } from '@orillusion/core';

// An sample of dynamically updating a geometry vertex attribute
class Smaple_VertexAnimation {
    // This geometry will dynamically update its vertex data over time
    floorGeometry: PlaneGeometry;
    scene: Scene3D;
    async run() {
        await Engine3D.init({ beforeRender: () => this.update() });

        let view = new View3D();
        view.scene = new Scene3D();
        view.scene.addComponent(AtmosphericComponent);

        this.scene = view.scene;
        view.camera = CameraUtil.createCamera3DObject(view.scene, 'camera');
        view.camera.perspective(60, Engine3D.aspect, 1, 2000);
        view.camera.object3D.addComponent(HoverCameraController).setCamera(35, -20, 150);

        Engine3D.startRenderView(view);

        this.createScene();
    }

    private createScene() {
        // add light
        let lightObj3D = new Object3D();
        let directLight = lightObj3D.addComponent(DirectLight);
        directLight.intensity = 25;
        directLight.lightColor = KelvinUtil.color_temperature_to_rgb(5355);
        directLight.castShadow = true;
        lightObj3D.rotationX = 53.2;
        lightObj3D.rotationY = 220;
        lightObj3D.rotationZ = 5.58;
        this.scene.addChild(lightObj3D);

        // add floor
        this.floorGeometry = new PlaneGeometry(100, 100, 199, 199);
        let floor = new Object3D();
        let renderer = floor.addComponent(MeshRenderer);
        renderer.geometry = this.floorGeometry;
        renderer.material = new LitMaterial();
        renderer.castShadow = true;
        renderer.receiveShadow = true;
        this.scene.addChild(floor);
    }

    private update() {
        if (this.floorGeometry) {
            let posAttrData = this.floorGeometry.getAttribute(VertexAttributeName.position);
            // update its vertex data over time
            let timeOffset = Time.time;
            for (let i = 0, count = posAttrData.data.length / 3; i < count; i++) {
                posAttrData.data[i * 3 + 1] = Math.sin(timeOffset * 0.01 + i * 0.25);
            }
            // position attr need to be upload
            this.floorGeometry.vertexBuffer.upload(VertexAttributeName.position, posAttrData);
            //update normals
            this.floorGeometry.computeNormals();
        }
    }
}

new Smaple_VertexAnimation().run();

几何体扩展

除了上述内置的常见几何体,引擎还提供了 @orillusion/geometry 几何体扩展包,目前包含字体几何体 TextGeometry, 2D挤压缓冲几何体ExtrudeGeometry, 地形几何体 TerrainGeometry 等扩展和工具类。

用户可以通过 NPM 包快速引入几何体插件:

bash
npm install @orillusion/core --save
npm install @orillusion/geometry --save
ts
import { Engine3D } from "@orillusion/core"
import { TextGeometry } from "@orillusion/geometry"

2D挤压缓冲

和内置的 ExtrudeGeometry 类似但不同,该扩展类简化了数据,支持从 Shape2D 平面图形中生成空间几何体。

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, View3D, Scene3D, CameraUtil, AtmosphericComponent, webGPUContext, HoverCameraController, Object3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer, Color, GridObject, Vector2, Vector3 } from "@orillusion/core";
import { Shape2D, ExtrudeGeometry, Path2D } from "@orillusion/geometry";

class Sample_ExtrudeGeometry {
    scene: Scene3D
    async run() {
        await Engine3D.init();
        let view = new View3D();
        view.scene = this.scene = new Scene3D();
        let sky = view.scene.addComponent(AtmosphericComponent);

        view.camera = CameraUtil.createCamera3DObject(view.scene);
        view.camera.perspective(60, webGPUContext.aspect, 1, 5000.0);
        view.camera.object3D.z = -15;
        view.camera.object3D.addComponent(HoverCameraController).setCamera(0, -20, 500);

        let lightObj3D = new Object3D();
        let sunLight = lightObj3D.addComponent(DirectLight);
        sunLight.intensity = 3;
        sunLight.lightColor = KelvinUtil.color_temperature_to_rgb(6553);
        sunLight.castShadow = true;
        lightObj3D.rotationX = 53.2;
        lightObj3D.rotationY = 220;
        lightObj3D.rotationZ = 5.58;
        view.scene.addChild(lightObj3D);
        sky.relativeTransform = lightObj3D.transform;

        view.scene.addChild(new GridObject(1000, 100))

        Engine3D.startRenderView(view);

        this.createShapes();
    }

    async createShapes() {
        
        // california
        {
            let points:Vector2[] = []
            points.push( new Vector2( 610, 320 ) );
            points.push( new Vector2( 450, 300 ) );
            points.push( new Vector2( 392, 392 ) );
            points.push( new Vector2( 266, 438 ) );
            points.push( new Vector2( 190, 570 ) );
            points.push( new Vector2( 190, 600 ) );
            points.push( new Vector2( 160, 620 ) );
            points.push( new Vector2( 160, 650 ) );
            points.push( new Vector2( 180, 640 ) );
            points.push( new Vector2( 165, 680 ) );
            points.push( new Vector2( 150, 670 ) );
            points.push( new Vector2( 90, 737 ) );
            points.push( new Vector2( 80, 795 ) );
            points.push( new Vector2( 50, 835 ) );
            points.push( new Vector2( 64, 870 ) );
            points.push( new Vector2( 60, 945 ) );
            points.push( new Vector2( 300, 945 ) );
            points.push( new Vector2( 300, 743 ) );
            points.push( new Vector2( 600, 473 ) );
            points.push( new Vector2( 626, 425 ) );
            points.push( new Vector2( 600, 370 ) );
            points.push( new Vector2( 610, 320 ) );

            let shape = new Shape2D(points.map(p=>p.multiplyScaler(0.25)))
            this.addShape(shape, -300, -60, 0)
        }

        // triangle
        {
            let shape = new Shape2D();
            shape.moveTo(80, 20);
            shape.lineTo(40, 80);
            shape.lineTo(120, 80);
            shape.lineTo(80, 20);

            this.addShape(shape, -180, 0, 0)
        }

        // heart
        {
            const x = 0, y = 0;
            const shape = new Shape2D()
                .moveTo( x + 25, y + 25 )
                .bezierCurveTo( x + 25, y + 25, x + 20, y, x, y )
                .bezierCurveTo( x - 30, y, x - 30, y + 35, x - 30, y + 35 )
                .bezierCurveTo( x - 30, y + 55, x - 10, y + 77, x + 25, y + 95 )
                .bezierCurveTo( x + 60, y + 77, x + 80, y + 55, x + 80, y + 35 )
                .bezierCurveTo( x + 80, y + 35, x + 80, y, x + 50, y )
                .bezierCurveTo( x + 35, y, x + 25, y + 25, x + 25, y + 25 );


            this.addShape(shape, 0, 20, 0)
        }

        // square
        {
            const sqLength = 80;
            const squareShape = new Shape2D()
                .moveTo( 0, 0 )
                .lineTo( 0, sqLength )
                .lineTo( sqLength, sqLength )
                .lineTo( sqLength, 0 )
                .lineTo( 0, 0 );
            
            this.addShape(squareShape, 100, 20, 0)
        }

        // Circle
        {
            const circleRadius = 40;
            const circleShape = new Shape2D()
                .moveTo( 0, circleRadius )
                .quadraticCurveTo( circleRadius, circleRadius, circleRadius, 0 )
                .quadraticCurveTo( circleRadius, - circleRadius, 0, - circleRadius )
                .quadraticCurveTo( - circleRadius, - circleRadius, - circleRadius, 0 )
                .quadraticCurveTo( - circleRadius, circleRadius, 0, circleRadius );

            this.addShape(circleShape, 140, 150, 0)
        }

        // Fish
        {
            const x = 0, y =0;
            const fishShape = new Shape2D()
                .moveTo( x, y )
                .quadraticCurveTo( x + 50, y - 80, x + 90, y - 10 )
                .quadraticCurveTo( x + 100, y - 10, x + 115, y - 40 )
                .quadraticCurveTo( x + 115, y, x + 115, y + 40 )
                .quadraticCurveTo( x + 100, y + 10, x + 90, y + 10 )
                .quadraticCurveTo( x + 50, y + 80, x, y );

            this.addShape(fishShape, -40, 160, 0)
        }

        // holes
        {
            const sqLength = 80;
            const squareShape = new Shape2D()
                .moveTo( 0, 0 )
                .lineTo( 0, sqLength )
                .lineTo( sqLength, sqLength )
                .lineTo( sqLength, 0 )
                .lineTo( 0, 0 );

            let hole1 = new Path2D()
                .moveTo( 10, 10 )
                .lineTo( 10, 30 )
                .lineTo( 30, 30 )
                .lineTo( 30, 10 )
                .lineTo( 10, 10 );
            
            let hole2 = new Path2D()
                .moveTo( 40, 10 )
                .lineTo( 40, 30 )
                .lineTo( 60, 30 )
                .lineTo( 60, 10 )
                .lineTo( 40, 10 );
            
            squareShape.holes.push(hole1, hole2);

            this.addShape(squareShape, -150, 100, 0)
        }

    }

    matrial: LitMaterial;
    addShape(shape: Shape2D, x:number = 0, y:number = 0, z:number = 0){
        let obj = new Object3D();
        obj.localPosition = new Vector3(x, y, z)
        let mr = obj.addComponent(MeshRenderer);
        mr.geometry = new ExtrudeGeometry([shape], {
            depth: 10,
            bevelEnabled: false,
            steps: 1
        });
        if(!this.matrial){
            let mat =  this.matrial = new LitMaterial();
            mat.baseColor = new Color(0.2, 0.5, 1.0);
            mat.castShadow = false;
        }
        let mats = [];
        for (let i = 0; i < mr.geometry.subGeometries.length; i++) {
            mats.push(this.matrial);
        }
        mr.materials = mats;
        this.scene.addChild(obj);
    }
}

new Sample_ExtrudeGeometry().run();

字体几何体

支持从常见的字体文件 woff,otf,ttf 中创建空间文字几何体

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, View3D, Scene3D, CameraUtil, AtmosphericComponent, webGPUContext, HoverCameraController, Object3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer } from "@orillusion/core";
import { TextGeometry, FontParser } from "@orillusion/geometry";
import { Graphic3D } from "@orillusion/graphic";

class Sample_TextGeometry {
    lightObj: Object3D;
    async run() {
        await Engine3D.init();
        let view = new View3D();
        view.scene = new Scene3D();
        let sky = view.scene.addComponent(AtmosphericComponent);

        view.camera = CameraUtil.createCamera3DObject(view.scene);
        view.camera.perspective(60, webGPUContext.aspect, 1, 5000.0);
        view.camera.object3D.z = -15;
        view.camera.object3D.addComponent(HoverCameraController).setCamera(35, -20, 150);

        Engine3D.startRenderView(view);

        await this.createScene(view.scene);
        sky.relativeTransform = this.lightObj.transform;
    }

    async createScene(scene: Scene3D) {
        {
            scene.addChild(new Graphic3D());

            let font = await Engine3D.res.load("https://cdn.orillusion.com/fonts/Roboto.ttf", FontParser);

            let obj = new Object3D();
            let mr = obj.addComponent(MeshRenderer);
            mr.geometry = new TextGeometry("Hello, Orillusion!", {
                font: font, // required
                fontSize: 16, // required
                depth: 2.5,
                steps: 1,
                bevelEnabled: false
            });

            let mats = [];
            let mat = new LitMaterial();
            for (let i = 0; i < mr.geometry.subGeometries.length; i++) {
                mats.push(mat);
            }
            mr.materials = mats;

            obj.x = mr.geometry.bounds.size.x * -0.5;

            scene.addChild(obj);
        }

        let lightObj3D = this.lightObj = new Object3D();
        let sunLight = lightObj3D.addComponent(DirectLight);
        sunLight.intensity = 3;
        sunLight.lightColor = KelvinUtil.color_temperature_to_rgb(6553);
        sunLight.castShadow = true;
        lightObj3D.rotationX = 53.2;
        lightObj3D.rotationY = 220;
        lightObj3D.rotationZ = 5.58;
        scene.addChild(lightObj3D);
    }
}

new Sample_TextGeometry().run();

地形几何体

支持通过 2D 高度图或高程图快速创建 3D 地形几何体

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, View3D, Scene3D, CameraUtil, AtmosphericComponent, webGPUContext, HoverCameraController, Object3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer, PostProcessingComponent, BitmapTexture2D, GlobalFog, Color } from '@orillusion/core';
import { TerrainGeometry } from '@orillusion/geometry';
import { Stats } from '@orillusion/stats';

// An sample of custom vertex attribute of geometry
class Sample_Terrain {
    view: View3D;
    post: PostProcessingComponent;

    async run() {
        Engine3D.setting.shadow.autoUpdate = true;
        Engine3D.setting.shadow.updateFrameRate = 1;
        Engine3D.setting.shadow.shadowBound = 500;
        Engine3D.setting.shadow.shadowSize = 2048;

        await Engine3D.init();
        this.view = new View3D();
        this.view.scene = new Scene3D();
        this.view.scene.addComponent(AtmosphericComponent);
        this.view.scene.addComponent(Stats);

        this.view.camera = CameraUtil.createCamera3DObject(this.view.scene);
        this.view.camera.perspective(60, webGPUContext.aspect, 1, 50000.0);
        this.view.camera.object3D.z = -15;
        this.view.camera.object3D.addComponent(HoverCameraController).setCamera(35, -20, 10000);

        Engine3D.startRenderView(this.view);

        this.post = this.view.scene.addComponent(PostProcessingComponent);
        let fog = this.post.addPost(GlobalFog);
        fog.start = 2000;
        fog.end = 0;
        fog.fogHeightScale = 0.116;
        fog.density = 0.094;
        fog.ins = 0.1041;
        fog.skyFactor = 0.35;
        fog.overrideSkyFactor = 0.7;

        fog.fogColor = new Color(136 / 255, 215 / 255, 236 / 255, 1);
        fog.fogHeightScale = 0.1;
        fog.falloff = 0.626;
        fog.scatteringExponent = 8;
        fog.dirHeightLine = 6.5;

        this.createScene(this.view.scene);
    }

    private async createScene(scene: Scene3D) {
        {
            let sunObj = new Object3D();
            let sunLight = sunObj.addComponent(DirectLight);
            sunLight.lightColor = KelvinUtil.color_temperature_to_rgb(6553);
            sunLight.castShadow = true;
            sunLight.intensity = 4;
            sunObj.transform.rotationX = 50;
            sunObj.transform.rotationY = 50;
            scene.addChild(sunObj);
        }

        //bitmap
        let bitmapTexture = await Engine3D.res.loadTexture('https://cdn.orillusion.com/terrain/test01/bitmap.png');
        let heightTexture = await Engine3D.res.loadTexture('https://cdn.orillusion.com/terrain/test01/height.png');
        let terrainSizeW = 20488;
        let terrainSizeH = 20488;
        let terrainGeometry: TerrainGeometry;
        {
            let mat = new LitMaterial();
            terrainGeometry = new TerrainGeometry(terrainSizeW, terrainSizeH, 2000, 2000);
            terrainGeometry.setHeight(heightTexture as BitmapTexture2D, 5000);
            let floor = new Object3D();
            let mr = floor.addComponent(MeshRenderer);
            mr.geometry = terrainGeometry;
            mat.baseMap = bitmapTexture;
            mr.material = mat;
            scene.addChild(floor);
        }
    }
}

new Sample_Terrain().run();

草地几何体

创建模拟草地地形,配合草地组件可以模拟动态随风摆动效果

WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
ts
import { Engine3D, View3D, Scene3D, CameraUtil, AtmosphericComponent, webGPUContext, HoverCameraController, Object3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer, Vector3, PostProcessingComponent, BitmapTexture2D, GlobalFog, Color } from '@orillusion/core';
import { GrassComponent, TerrainGeometry } from '@orillusion/geometry';
import { Stats } from '@orillusion/stats';
import dat from 'dat.gui';

class Sample_Grass {
    view: View3D;
    post: PostProcessingComponent;

    async run() {
        Engine3D.setting.shadow.autoUpdate = true;
        Engine3D.setting.shadow.updateFrameRate = 1;
        Engine3D.setting.shadow.shadowBound = 500;
        Engine3D.setting.shadow.shadowSize = 1024;

        await Engine3D.init();
        this.view = new View3D();
        this.view.scene = new Scene3D();
        this.view.scene.addComponent(AtmosphericComponent);
        this.view.scene.addComponent(Stats);

        this.view.camera = CameraUtil.createCamera3DObject(this.view.scene);
        this.view.camera.enableCSM = true;
        this.view.camera.perspective(60, webGPUContext.aspect, 1, 5000.0);
        this.view.camera.object3D.addComponent(HoverCameraController).setCamera(35, -20, 500);

        Engine3D.startRenderView(this.view);
        this.createScene(this.view.scene);
    }

    private async createScene(scene: Scene3D) {
        //bitmap
        let bitmapTexture = await Engine3D.res.loadTexture('https://cdn.orillusion.com/terrain/test01/bitmap.png');
        let heightTexture = await Engine3D.res.loadTexture('https://cdn.orillusion.com/terrain/test01/height.png');
        // let grassTexture = await Engine3D.res.loadTexture('https://cdn.orillusion.com/terrain/grass/GrassThick.png');
        let gustNoiseTexture = await Engine3D.res.loadTexture('https://cdn.orillusion.com/terrain/grass/displ_noise_curl_1.png');
        let sunObj = new Object3D();
        let sunLight = sunObj.addComponent(DirectLight);
        sunLight.lightColor = KelvinUtil.color_temperature_to_rgb(6553);
        sunLight.castShadow = true;
        sunLight.intensity = 49;
        sunObj.transform.rotationX = 50;
        sunObj.transform.rotationY = 50;
        scene.addChild(sunObj);

        let terrainSize = 1000;
        let size = 1000;
        let grassCount = 6795;
        // let grassCount = 10;
        let des = 1;
        let space = 2;
        let terrainGeometry: TerrainGeometry;
        {
            let mat = new LitMaterial();
            terrainGeometry = new TerrainGeometry(terrainSize, terrainSize);
            terrainGeometry.setHeight(heightTexture as BitmapTexture2D, 100);
            let floor = new Object3D();
            let mr = floor.addComponent(MeshRenderer);
            mr.geometry = terrainGeometry;
            mat.baseMap = bitmapTexture;
            mr.material = mat;
            scene.addChild(floor);
        }

        let grassCom: GrassComponent;
        {
            let grass = new Object3D();
            grassCom = grass.addComponent(GrassComponent);
            grassCom.setGrassTexture(Engine3D.res.whiteTexture);
            // grassCom.setGrassTexture(grassTexture);
            grassCom.setWindNoiseTexture(gustNoiseTexture);
            grassCom.setGrass(18, 1, 5, 1, grassCount);

            let tsw = terrainSize / terrainGeometry.segmentW;
            let tsh = terrainSize / terrainGeometry.segmentH;
            let index = 0;
            terrainGeometry.greenData.forEach((data) => {
                for (let d = 0; d < des; d++) {
                    let node = grassCom.nodes[index++];
                    if (node) {
                        let px = data.x * tsw - terrainSize * 0.5 + Math.random() * space - space * 0.5;
                        let pz = data.z * tsh - terrainSize * 0.5 + Math.random() * space - space * 0.5;
                        let pos = new Vector3(px, 0, pz);

                        let tw = terrainGeometry.segmentW;
                        let th = terrainGeometry.segmentH;
                        let tx = Math.floor(((pos.x + size * 0.5) / size) * terrainGeometry.segmentW);
                        let tz = Math.floor(((pos.z + size * 0.5) / size) * terrainGeometry.segmentH);

                        if (terrainGeometry.heightData.length > tz && terrainGeometry.heightData[tz].length > tx) {
                            pos.y = terrainGeometry.heightData[tz][tx];
                        }

                        let gassSize = 0.8;
                        let scale = (Math.random() * 0.75 + 0.25) * gassSize;
                        node.localPosition = pos;
                        node.localRotation.y = Math.random() * 360;
                        node.localScale = new Vector3(scale, scale, scale);
                        node.updateWorldMatrix(true);
                    }
                }
            });
            scene.addChild(grass);
        }

        let gui = new dat.GUI()
        let dir = gui.addFolder('grass-wind')
        dir.addColor(grassCom.grassMaterial.grassBaseColor, 'rgba').name('grassBaseColor').onChange(val=>{
            let color = grassCom.grassMaterial.grassBaseColor
            color['rgba'] = val
            grassCom.grassMaterial.grassBaseColor = color
        })
        dir.addColor(grassCom.grassMaterial.grassTopColor, 'rgba').name('grassTopColor').onChange(val=>{
            let color = grassCom.grassMaterial.grassBaseColor
            color['rgba'] = val
            grassCom.grassMaterial.grassTopColor = color
        })
        dir.add(grassCom.grassMaterial.windDirection, 'x', -1.0, 1, 0.0001).onChange((v) => {
            let tv = grassCom.grassMaterial.windDirection
            tv.x = v
            grassCom.grassMaterial.windDirection = tv
        })
        dir.add(grassCom.grassMaterial.windDirection, 'y', -1.0, 1, 0.0001).onChange((v) => {
            let tv = grassCom.grassMaterial.windDirection
            tv.y = v
            grassCom.grassMaterial.windDirection = tv
        })
        dir.add(grassCom.grassMaterial, 'windPower', 0.0, 20, 0.0001)
        dir.add(grassCom.grassMaterial, 'windSpeed', 0.0, 20, 0.0001)
        dir.add(grassCom.grassMaterial, 'curvature', 0.0, 1, 0.0001)
        dir.add(grassCom.grassMaterial, 'grassHeight', 0.0, 100, 0.0001)
        dir.add(grassCom.grassMaterial, 'roughness', 0.0, 1, 0.0001)
        dir.add(grassCom.grassMaterial, 'translucent', 0.0, 1, 0.0001)
        dir.add(grassCom.grassMaterial, 'soft', 0.0, 10, 0.0001)
        dir.add(grassCom.grassMaterial, 'specular', 0.0, 10, 0.0001)
        dir.open()
    }
}

new Sample_Grass().run();