<六>CocosCreator3D && 2D混合

在3D场景中2DUI和3D的混合需要用到一系列渲染纹理的技术。

纹理

在开发中,纹理贴图资源是一种用于程序采样的资源,如模型上的贴图、精灵上的 UI。当程序渲染 UI 或者模型时,会使用纹理坐标获取纹理颜色,然后填充在模型网格上,再加上光照等等一系列处理便渲染出了整个场景。
关于纹理:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/#_1

渲染纹理

渲染纹理是一张在 GPU 上的纹理。通常我们会把它设置到相机的 目标纹理 上,使相机照射的内容通过离屏的 frambuffer 绘制到该纹理上。一般可用于制作汽车后视镜,动态阴影等功能。

创建渲染纹理资源

在使用渲染纹理前需要先创建渲染纹理资源,在资源管理器 中点击左上方的 + 按钮,然后选择 渲染纹理。

在属性检查器中可设置渲染纹理的相关属性。

一般来说,会把纹理贴图设置到相机的渲染输出目标贴图上。

渲染纹理在2D/UI中使用

备注:无论渲染纹理在哪种情况下使用,都需要至少两个或以上相机。渲染纹理作为某相机的输出目标纹理时,不能再做为该相机的要渲染的纹理,不然会宝N多错误。最少创建两个相机,一个相机作为输入,把渲染的内容绘制到目标渲染纹理上,另一个相机渲染的对象(如精灵作为输出)纹理设置为渲染纹理。
RenderTexture 可以像普通贴图一样使用。
以 Sprite 为例:
在场景中创建一个精灵,从 资源管理器 拖拽到精灵的 SpriteFrame 属性。
在场景中创建几个3D物体,运行看一下效果:

作为材质贴图使用

将 RenderTexture 设置为材质贴图包括三个步骤:

1.在 effect 中处理 uv。判断 SAMPLE_FROM_RT,并调用 CC_HANDLE_RT_SAMPLE_FLIP 函数:

#if USE_TEXTURE
    v_uv = a_texCoord * tilingOffset.xy + tilingOffset.zw;
    #if SAMPLE_FROM_RT
        CC_HANDLE_RT_SAMPLE_FLIP(v_uv);
    #endif
#endif

完整的effect:

// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
  techniques:
  - name: opaque
    passes:
    - vert: camera-texture-vs:vert
      frag: camera-texture-fs:frag
      properties: &props
        mainTexture:    { value: grey }
        tilingOffset:   { value: [1, 1, 0, 0] }
        mainColor:      { value: [1, 1, 1, 1], editor: { type: color } }
        colorScale:     { value: [1, 1, 1], target: colorScaleAndCutoff.xyz }
        alphaThreshold: { value: 0.5, target: colorScaleAndCutoff.w, editor: { parent: USE_ALPHA_TEST } }
        color:          { target: mainColor, editor: { visible: false } } # backward compability
      migrations: &migs
        properties:
          mainColor:    { formerlySerializedAs: color }
  - name: transparent
    passes:
    - vert: camera-texture-vs:vert
      frag: camera-texture-fs:frag
      depthStencilState: &d1
        depthTest: true
        depthWrite: false
      blendState:
        targets:
        - blend: true
          blendSrc: src_alpha
          blendDst: one_minus_src_alpha
          blendDstAlpha: one_minus_src_alpha
      properties: *props
      migrations: *migs
  - name: add
    passes:
    - vert: camera-texture-vs:vert
      frag: camera-texture-fs:frag
      rasterizerState: &r1 { cullMode: none }
      depthStencilState: *d1
      blendState:
        targets:
        - blend: true
          blendSrc: src_alpha
          blendDst: one
          blendSrcAlpha: src_alpha
          blendDstAlpha: one
      properties: *props
      migrations: *migs
  - name: alpha-blend
    passes:
    - vert: camera-texture-vs:vert
      frag: camera-texture-fs:frag
      rasterizerState: *r1
      depthStencilState: *d1
      blendState:
        targets:
        - blend: true
          blendSrc: src_alpha
          blendDst: one_minus_src_alpha
          blendSrcAlpha: src_alpha
          blendDstAlpha: one_minus_src_alpha
      properties: *props
      migrations: *migs
}%

CCProgram camera-texture-vs %{
  precision highp float;
  #include <legacy/input>
  #include <builtin/uniforms/cc-global>
  #include <legacy/decode-base>
  #include <legacy/local-batch>
  #include <legacy/input>

  #if USE_VERTEX_COLOR
    in lowp vec4 a_color;
    out lowp vec4 v_color;
  #endif

  #if USE_TEXTURE
    out vec2 v_uv;
    uniform TexCoords {
      vec4 tilingOffset;
    };
  #endif

  vec4 vert () {
    vec4 position;
    CCVertInput(position);

    mat4 matWorld;
    CCGetWorldMatrix(matWorld);

    #if USE_TEXTURE
      v_uv = a_texCoord * tilingOffset.xy + tilingOffset.zw;
      #if SAMPLE_FROM_RT
        CC_HANDLE_RT_SAMPLE_FLIP(v_uv);
      #endif
    #endif

    #if USE_VERTEX_COLOR
      v_color = a_color;
    #endif

    return cc_matProj * (cc_matView * matWorld) * position;
  }
}%

CCProgram camera-texture-fs %{
  precision highp float;
  #include <legacy/output>

  #if USE_ALPHA_TEST
    #pragma define-meta ALPHA_TEST_CHANNEL options([a, r, g, b])
  #endif

  #if USE_TEXTURE
    in vec2 v_uv;
    uniform sampler2D mainTexture;
  #endif

  uniform Constant {
    vec4 mainColor;
    vec4 colorScaleAndCutoff;
  };

  #if USE_VERTEX_COLOR
    in lowp vec4 v_color;
  #endif

  vec4 frag () {
    vec4 o = mainColor;
    o.rgb *= colorScaleAndCutoff.xyz;

    #if USE_VERTEX_COLOR
      o *= v_color;
    #endif

    #if USE_TEXTURE
      o *= texture(mainTexture, v_uv);
    #endif

    #if USE_ALPHA_TEST
      if (o.ALPHA_TEST_CHANNEL < colorScaleAndCutoff.w) discard;
    #endif

    return CCFragOutput(o);
  }
}%

运行:

把UI绘制到模型上

1.创建一个四方形,用于绘制rt
新建一个材质就叫ui-to-m吧,shader用内置的builtin-camera-texture,该effect已经处理好了设置rt的逻辑。
2.创建要待渲染的ui,把canvas就重命名为rendertexture好了,把该ui的相机重命名为UICamera_rendertexture。(重命名只是为了顾名思义)
3.创建一个把ui绘制到model的脚本,就叫RenderUIToModel,挂载到rendertexture上
编辑脚本:
创建一个目标模型的引用属性:

@property(MeshRenderer)
    public model: MeshRenderer = null!;

创建一个rt的私有属性
实现就放在start中好了:

import { _decorator, Canvas, Component, MeshRenderer, Node, RenderTexture, view } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('RenderUIToModel')
export class RenderUIToModel extends Component {
    @property(MeshRenderer)
    public model: MeshRenderer = null!;

    public renderTexture: RenderTexture | null = null;
    start() {
        //获取当前Canvas
        const canvas = this.getComponent(Canvas);
        //实例化一个渲染纹理
        const tex = new RenderTexture();
        tex.name = 'render-ui-to-model';
        //设置渲染纹理的宽高
        const size = view.getVisibleSize();
        tex.reset({
            width: size.width,
            height: size.height,
        });
        this.renderTexture = tex;
        //指定相机的输出贴图
        canvas.cameraComponent.targetTexture = tex;
        //指定目标模型的贴图
        this.model.material.setProperty('mainTexture', tex);
    }
    protected onDestroy(): void {
        if (this.renderTexture) {
            this.renderTexture.destroy();
            this.renderTexture = null;
        }
    }

    update(deltaTime: number) {

    }
}


运行预览:

通过readPixels方法实现3D内容绘制到2D中

readPixels方法是一个用于从渲染目标(例如屏幕)读取像素数据的方法。这个方法可以用来捕获当前渲染窗口的内容,或者特定的渲染目标(如RenderTexture)的像素数据。这里可以把3D 相机映射的内容通过readPixels方法读取到ArrayBuffer。
在Cocos Creator中,ArrayBuffer 是一个二进制的大块数据的容器。它是一个固定长度的通用类型数组,可以用来存储不同类型的数据(如整数、浮点数等)。ArrayBuffer 主要用于处理二进制数据,特别是在与浏览器的WebGL API交互时非常有用,因为WebGL API需要直接访问二进制数据来进行渲染操作。
readPixels 方法读取屏幕或渲染目标的像素数据时,返回的是一个 ArrayBufferView,如 Uint8Array 或 Float32Array。
关于readPixels 有三个参数:
rect: Rect 类型的对象,定义了要读取的矩形区域。rect 包含四个属性:x, y, width, height。
targetBuffer: 可选参数,用于存储读取的像素数据的数组缓冲区。如果不提供,将会自动创建一个新的数组缓冲区。
format: 可选参数,指定读取的像素格式,默认为 PixelFormat.RGBA8888。
返回:
一个 ArrayBufferView 类型的对象,通常是一个 Uint8Array 或其他类型的视图,包含了指定区域的像素数据。
TODO

posted @ 2024-10-25 15:33  EricShx  阅读(260)  评论(0)    收藏  举报