1 前言

​ 本文主要介绍使用 Filament 实现纹理贴图,读者如果对 Filament 不太熟悉,请回顾以下内容。

​ Filament 纹理坐标的 x、y 轴正方向分别朝右和朝上,其 y 轴正方向朝向与 OpenGL ES 和 libGDX 相反(详见【OpenGL ES】纹理贴图【libGDX】Mesh纹理贴图),如下。

2 纹理贴图

​ 本文项目结构如下,完整代码资源 → Filament纹理贴图

2.1 自定义基类

​ 为方便读者将注意力聚焦在 Filament 的输入上,轻松配置复杂的环境依赖逻辑,笔者仿照 OpenGL ES 的写法,抽出了 FLSurfaceView 和 BaseModel 类。FLSurfaceView 与 GLSurfaceView 的功能类似,承载了渲染环境配置;BaseModel 中提供了一些 VertexBuffer、IndexBuffer、Material、Renderable 相关的工具类,方便子类直接使用这些工具类。

​ build.gradle

...
android {
...
aaptOptions { // 在应用程序打包过程中不压缩的文件
noCompress 'filamat', 'ktx'
}
} dependencies {
implementation fileTree(dir: '../libs', include: ['*.aar'])
...
}

​ 说明:在项目根目录下的 libs 目录中,需要放入以下 aar 文件,它们源自Filament环境搭建中编译生成的 aar。

​ FLSurfaceView.java

package com.zhyan8.texture.filament;

import android.content.Context;
import android.graphics.Point;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceView; import com.google.android.filament.Camera;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.Filament;
import com.google.android.filament.Renderer;
import com.google.android.filament.Scene;
import com.google.android.filament.Skybox;
import com.google.android.filament.SwapChain;
import com.google.android.filament.View;
import com.google.android.filament.Viewport;
import com.google.android.filament.android.DisplayHelper;
import com.google.android.filament.android.FilamentHelper;
import com.google.android.filament.android.UiHelper; import java.util.ArrayList; /*
* Filament中待渲染的SurfaceView
* 功能可以类比OpenGL ES中的GLSurfaceView
* 用于创建Filament的渲染环境
*/
public class FLSurfaceView extends SurfaceView {
public static int RENDERMODE_WHEN_DIRTY = 0; // 用户请求渲染才渲染一帧
public static int RENDERMODE_CONTINUOUSLY = 1; // 持续渲染
protected int mRenderMode = RENDERMODE_CONTINUOUSLY; // 渲染模式
protected Choreographer mChoreographer; // 消息控制
protected DisplayHelper mDisplayHelper; // 管理Display(可以监听分辨率或刷新率的变化)
protected UiHelper mUiHelper; // 管理SurfaceView、TextureView、SurfaceHolder
protected Engine mEngine; // 引擎(跟踪用户创建的资源, 管理渲染线程和硬件渲染器)
protected Renderer mRenderer; // 渲染器(用于操作系统窗口, 生成绘制命令, 管理帧延时)
protected Scene mScene; // 场景(管理渲染对象、灯光)
protected View mView; // 存储渲染数据(View是Renderer操作的对象)
protected Camera mCamera; // 相机(视角管理)
protected Point mDesiredSize; // 渲染分辨率
protected float[] mSkyboxColor; // 背景颜色
protected SwapChain mSwapChain; // 操作系统的本地可渲染表面(native renderable surface, 通常是一个window或view)
protected FrameCallback mFrameCallback = new FrameCallback(); // 帧回调
protected ArrayList<RenderCallback> mRenderCallbacks; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等) static {
Filament.init();
} public FLSurfaceView(Context context) {
super(context);
mChoreographer = Choreographer.getInstance();
mDisplayHelper = new DisplayHelper(context);
mRenderCallbacks = new ArrayList<>();
} public void init() { // 初始化
setupSurfaceView();
setupFilament();
setupView();
setupScene();
} public void setRenderMode(int renderMode) { // 设置渲染模式
mRenderMode = renderMode;
} public void addRenderCallback(RenderCallback renderCallback) { // 添加渲染回调
if (renderCallback != null) {
mRenderCallbacks.add(renderCallback);
}
} public void requestRender() { // 请求渲染
mChoreographer.postFrameCallback(mFrameCallback);
} public void onResume() { // 恢复
mChoreographer.postFrameCallback(mFrameCallback);
} public void onPause() { // 暂停
mChoreographer.removeFrameCallback(mFrameCallback);
} public void onDestroy() { // 销毁Filament环境
mChoreographer.removeFrameCallback(mFrameCallback);
mRenderCallbacks.clear();
mUiHelper.detach();
mEngine.destroyRenderer(mRenderer);
mEngine.destroyView(mView);
mEngine.destroyScene(mScene);
mEngine.destroyCameraComponent(mCamera.getEntity());
EntityManager entityManager = EntityManager.get();
entityManager.destroy(mCamera.getEntity());
mEngine.destroy();
} protected void setupScene() { // 设置Scene参数
} protected void onResized(int width, int height) { // Surface尺寸变化时回调
double zoom = 1;
double aspect = (double) width / (double) height;
mCamera.setProjection(Camera.Projection.ORTHO,
-aspect * zoom, aspect * zoom, -zoom, zoom, 0, 1000);
} private void setupSurfaceView() { // 设置SurfaceView
mUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
mUiHelper.setRenderCallback(new SurfaceCallback());
if (mDesiredSize != null) {
mUiHelper.setDesiredSize(mDesiredSize.x, mDesiredSize.y);
}
mUiHelper.attachTo(this);
} private void setupFilament() { // 设置Filament参数
mEngine = Engine.create();
// mEngine = (new Engine.Builder()).featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build();
mRenderer = mEngine.createRenderer();
mScene = mEngine.createScene();
mView = mEngine.createView();
mCamera = mEngine.createCamera(mEngine.getEntityManager().create());
} private void setupView() { // 设置View参数
float[] color = mSkyboxColor != null ? mSkyboxColor : new float[] {0, 0, 0, 1};
Skybox skybox = (new Skybox.Builder()).color(color).build(mEngine);
mScene.setSkybox(skybox);
if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
mView.setPostProcessingEnabled(false); // FEATURE_LEVEL_0不支持post-processing
}
mView.setCamera(mCamera);
mView.setScene(mScene);
} /*
* 帧回调
*/
private class FrameCallback implements Choreographer.FrameCallback {
@Override
public void doFrame(long frameTimeNanos) { // 渲染每帧数据
if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
mChoreographer.postFrameCallback(this); // 请求下一帧
}
mRenderCallbacks.forEach(callback -> callback.onCall());
if (mUiHelper.isReadyToRender()) {
if (mRenderer.beginFrame(mSwapChain, frameTimeNanos)) {
mRenderer.render(mView);
mRenderer.endFrame();
}
}
}
} /*
* Surface回调
*/
private class SurfaceCallback implements UiHelper.RendererCallback {
@Override
public void onNativeWindowChanged(Surface surface) { // Native窗口改变时回调
if (mSwapChain != null) {
mEngine.destroySwapChain(mSwapChain);
}
long flags = mUiHelper.getSwapChainFlags();
if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
if (SwapChain.isSRGBSwapChainSupported(mEngine)) {
flags = flags | SwapChain.CONFIG_SRGB_COLORSPACE;
}
}
mSwapChain = mEngine.createSwapChain(surface, flags);
mDisplayHelper.attach(mRenderer, getDisplay());
} @Override
public void onDetachedFromSurface() { // 解绑Surface时回调
mDisplayHelper.detach();
if (mSwapChain != null) {
mEngine.destroySwapChain(mSwapChain);
mEngine.flushAndWait();
mSwapChain = null;
}
} @Override
public void onResized(int width, int height) { // Surface尺寸变化时回调
mView.setViewport(new Viewport(0, 0, width, height));
FilamentHelper.synchronizePendingFrames(mEngine);
FLSurfaceView.this.onResized(width, height);
}
} /*
* 每一帧渲染前的回调
* 一般用于处理模型变换、相机变换等
*/
public static interface RenderCallback {
void onCall();
}
}

​ BaseModel.java

package com.zhyan8.texture.filament;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.IndexBuffer;
import com.google.android.filament.Material;
import com.google.android.filament.MaterialInstance;
import com.google.android.filament.RenderableManager;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.Texture;
import com.google.android.filament.TransformManager;
import com.google.android.filament.VertexBuffer;
import com.google.android.filament.VertexBuffer.AttributeType;
import com.google.android.filament.VertexBuffer.VertexAttribute;
import com.google.android.filament.android.TextureHelper; import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; /*
* 模型基类
* 管理模型的材质、顶点属性、顶点索引、渲染id
*/
public class BaseModel {
private static String TAG = "BaseModel";
protected Context mContext; // 上下文
protected Engine mEngine; // Filament引擎
protected TransformManager mTransformManager; // 模型变换管理器
protected Material mMaterial; // 模型材质
protected MaterialInstance mMaterialInstance; // 模型材质实例
protected VertexBuffer mVertexBuffer; // 顶点属性缓存
protected IndexBuffer mIndexBuffer; // 顶点索引缓存
protected int mRenderable; // 渲染id
protected int mTransformComponent; // 模型变换组件的id
protected Box mBox; // 渲染区域
protected FLSurfaceView.RenderCallback mRenderCallback; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等) public BaseModel(Context context, Engine engine) {
mContext = context;
mEngine = engine;
mTransformManager = mEngine.getTransformManager();
} public Material getMaterial() { // 获取材质
return mMaterial;
} public VertexBuffer getVertexBuffer() { // 获取顶点属性缓存
return mVertexBuffer;
} public IndexBuffer getIndexBuffer() { // 获取顶点索引缓存
return mIndexBuffer;
} public int getRenderable() { // 获取渲染id
return mRenderable;
} public FLSurfaceView.RenderCallback getRenderCallback() { // 获取渲染回调
return mRenderCallback;
} public void destroy() { // 销毁模型
mEngine.destroyEntity(mRenderable);
mEngine.destroyVertexBuffer(mVertexBuffer);
mEngine.destroyIndexBuffer(mIndexBuffer);
mEngine.destroyMaterialInstance(mMaterialInstance);
mEngine.destroyMaterial(mMaterial);
EntityManager entityManager = EntityManager.get();
entityManager.destroy(mRenderable);
} protected Material loadMaterial(String materialPath) { // 加载材质
Buffer buffer = readUncompressedAsset(mContext, materialPath);
if (buffer != null) {
Material material = (new Material.Builder()).payload(buffer, buffer.remaining()).build(mEngine);
mMaterialInstance = material.createInstance();
material.compile(
Material.CompilerPriorityQueue.HIGH,
Material.UserVariantFilterBit.ALL,
new Handler(Looper.getMainLooper()),
() -> Log.i(TAG, "Material " + material.getName() + " compiled."));
mEngine.flush();
return material;
}
return null;
} protected VertexBuffer getVertexBuffer(float[] values) { // 获取顶点属性缓存
ByteBuffer vertexData = getByteBuffer(values);
int vertexCount = values.length / 3;
int vertexSize = Float.BYTES * 3;
VertexBuffer vertexBuffer = new VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.build(mEngine);
vertexBuffer.setBufferAt(mEngine, 0, vertexData);
return vertexBuffer;
} protected VertexBuffer getVertexBuffer(VertexPosCol[] values) { // 获取顶点属性缓存
ByteBuffer vertexData = getByteBuffer(values);
int vertexCount = values.length;
int vertexSize = VertexPosCol.BYTES;
VertexBuffer vertexBuffer = new VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.COLOR, 0, AttributeType.UBYTE4, 3 * Float.BYTES, vertexSize)
.normalized(VertexAttribute.COLOR)
.build(mEngine);
vertexBuffer.setBufferAt(mEngine, 0, vertexData);
return vertexBuffer;
} protected VertexBuffer getVertexBuffer(VertexPosUV[] values) { // 获取顶点属性缓存
ByteBuffer vertexData = getByteBuffer(values);
int vertexCount = values.length;
int vertexSize = VertexPosUV.BYTES;
VertexBuffer vertexBuffer = new VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.UV0, 0, AttributeType.FLOAT2, 3 * Float.BYTES, vertexSize)
.build(mEngine);
vertexBuffer.setBufferAt(mEngine, 0, vertexData);
return vertexBuffer;
} protected IndexBuffer getIndexBuffer(short[] values) { // 获取顶点索引缓存
ByteBuffer indexData = getByteBuffer(values);
int indexCount = values.length;
IndexBuffer indexBuffer = new IndexBuffer.Builder()
.indexCount(indexCount)
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
.build(mEngine);
indexBuffer.setBuffer(mEngine, indexData);
return indexBuffer;
} protected int getRenderable(PrimitiveType primitiveType, int vertexCount) { // 获取渲染id
int renderable = EntityManager.get().create();
new RenderableManager.Builder(1)
.boundingBox(mBox)
.geometry(0, primitiveType, mVertexBuffer, mIndexBuffer, 0, vertexCount)
.material(0, mMaterialInstance)
.build(mEngine, renderable);
return renderable;
} protected Texture getTexture(String texturePath) { // 获取Texture
Bitmap bitmap = loadBitmapFromAsset(mContext, texturePath);
if (bitmap != null) {
return generateTexture(bitmap);
}
return null;
} protected Texture getTexture(int resourceId) { // 获取Texture
Bitmap bitmap = loadBitmapFromDrawable(resourceId);
if (bitmap != null) {
return generateTexture(bitmap);
}
return null;
} private Texture generateTexture(Bitmap bitmap) { // 生成Texture
Texture texture = new Texture.Builder()
.width(bitmap.getWidth())
.height(bitmap.getHeight())
.sampler(Texture.Sampler.SAMPLER_2D)
.format(Texture.InternalFormat.SRGB8_A8)
.levels(0xff)
.build(mEngine);
TextureHelper.setBitmap(mEngine, texture, 0, bitmap, new Handler(), () ->
Log.i(TAG, "getTexture, Bitmap is released.")
);
texture.generateMipmaps(mEngine);
return texture;
} private Buffer readUncompressedAsset(Context context, String assetPath) { // 加载资源
ByteBuffer dist = null;
try {
AssetFileDescriptor fd = context.getAssets().openFd(assetPath);
try(FileInputStream fis = fd.createInputStream()) {
dist = ByteBuffer.allocate((int) fd.getLength());
try (ReadableByteChannel src = Channels.newChannel(fis)) {
src.read(dist);
}
}
} catch (IOException e) {
e.printStackTrace();
}
if (dist != null) {
return dist.rewind();
}
return null;
} private Bitmap loadBitmapFromAsset(Context context, String assetPath) { // 从asset中加载bitmap
Bitmap bitmap = null;
try (InputStream inputStream = context.getAssets().open(assetPath)) {
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
} private Bitmap loadBitmapFromDrawable(int resourceId) { // 从drawable中加载bitmap
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPremultiplied = true;
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), resourceId, options);
return bitmap;
} private ByteBuffer getByteBuffer(float[] values) { // float数组转换为ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Float.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
byteBuffer.putFloat(values[i]);
}
byteBuffer.flip();
return byteBuffer;
} private ByteBuffer getByteBuffer(short[] values) { // short数组转换为ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Short.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
byteBuffer.putShort(values[i]);
}
byteBuffer.flip();
return byteBuffer;
} private ByteBuffer getByteBuffer(VertexPosCol[] values) { // VertexPosCol数组转换为ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosCol.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
values[i].put(byteBuffer);
}
byteBuffer.flip();
return byteBuffer;
} private ByteBuffer getByteBuffer(VertexPosUV[] values) { // VertexPosUV数组转换为ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosUV.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
values[i].put(byteBuffer);
}
byteBuffer.flip();
return byteBuffer;
} /*
* 顶点数据(位置+颜色)
* 包含顶点位置和颜色
*/
public static class VertexPosCol {
public static int BYTES = 16;
public float x;
public float y;
public float z;
public int color;
public VertexPosCol() {}
public VertexPosCol(float x, float y, float z, int color) {
this.x = x;
this.y = y;
this.z = z;
this.color = color;
} public ByteBuffer put(ByteBuffer buffer) { // VertexPosCol转换为ByteBuffer
buffer.putFloat(x);
buffer.putFloat(y);
buffer.putFloat(z);
buffer.putInt(color);
return buffer;
}
} /*
* 顶点数据(位置+纹理坐标)
* 包含顶点位置和纹理坐标
*/
public static class VertexPosUV {
public static int BYTES = 20;
public float x;
public float y;
public float z;
public float u;
public float v;
public VertexPosUV() {}
public VertexPosUV(float x, float y, float z, float u, float v) {
this.x = x;
this.y = y;
this.z = z;
this.u = u;
this.v = v;
} public ByteBuffer put(ByteBuffer buffer) { // VertexPosUV转换为ByteBuffer
buffer.putFloat(x);
buffer.putFloat(y);
buffer.putFloat(z);
buffer.putFloat(u);
buffer.putFloat(v);
return buffer;
}
}
}

2.2 纹理贴图实现

​ MainActivity.java

package com.zhyan8.texture;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.zhyan8.texture.filament.FLSurfaceView;

public class MainActivity extends AppCompatActivity {
private FLSurfaceView mFLSurfaceView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFLSurfaceView = new MyFLSurfaceView(this);
setContentView(mFLSurfaceView);
mFLSurfaceView.init();
mFLSurfaceView.setRenderMode(FLSurfaceView.RENDERMODE_CONTINUOUSLY);
} @Override
public void onResume() {
super.onResume();
mFLSurfaceView.onResume();
} @Override
public void onPause() {
super.onPause();
mFLSurfaceView.onPause();
} @Override
public void onDestroy() {
super.onDestroy();
mFLSurfaceView.onDestroy();
}
}

​ MyFLSurfaceView.java

package com.zhyan8.texture;

import android.content.Context;

import com.google.android.filament.Camera;
import com.zhyan8.texture.filament.BaseModel;
import com.zhyan8.texture.filament.FLSurfaceView; public class MyFLSurfaceView extends FLSurfaceView {
private BaseModel mMyModel;
public MyFLSurfaceView(Context context) {
super(context);
} public void init() {
mSkyboxColor = new float[] {0.965f, 0.941f, 0.887f, 1};
super.init();
} @Override
public void onDestroy() {
mMyModel.destroy();
super.onDestroy();
} @Override
protected void setupScene() { // 设置Scene参数
mMyModel = new Square(getContext(), mEngine);
mScene.addEntity(mMyModel.getRenderable());
} @Override
protected void onResized(int width, int height) {
mCamera.setProjection(Camera.Projection.ORTHO, -1, 1, -1, 1, 0, 10);
}
}

​ Square.java

package com.zhyan8.texture;

import android.content.Context;

import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.TextureSampler;
import com.zhyan8.texture.filament.BaseModel; public class Square extends BaseModel {
private String materialPath = "materials/square.filamat";
private String texturePath = "textures/1.jpg";
private VertexPosUV[] mVertices = new VertexPosUV[] {
new VertexPosUV(-1f, -1f, 0f, 0f, 0f), // 左下
new VertexPosUV(1f, -1f, 0f, 1f, 0f), // 右下
new VertexPosUV(1f, 1f, 0f, 1f, 1f), // 右上
new VertexPosUV(-1f, 1f, 0f, 0f, 1f) // 左上
};
private short[] mIndex = new short[] {0, 1, 2, 0, 2, 3}; public Square(Context context, Engine engine) {
super(context, engine);
init();
} private void init() {
mBox = new Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.01f);
mMaterial = loadMaterial(materialPath);
mMaterialInstance.setParameter("mainTex", getTexture(texturePath), new TextureSampler());
mVertexBuffer = getVertexBuffer(mVertices);
mIndexBuffer = getIndexBuffer(mIndex);
mRenderable = getRenderable(PrimitiveType.TRIANGLES, mIndex.length);
}
}

​ square.mat

material {
name : square, shadingModel : unlit, // 禁用所有lighting
// 自定义变量参数
parameters : [
{
type : sampler2d,
name : mainTex
}
],
// 顶点着色器入参MaterialVertexInputs中需要的顶点属性
requires : [
uv0
]
} fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material); // 在方法返回前必须回调该函数
material.baseColor = texture(materialParams_mainTex, getUV0());
}
}

​ transform.bat

@echo off
setlocal enabledelayedexpansion
set "srcFolder=../src/main/materials"
set "distFolder=../src/main/assets/materials" for %%f in ("%srcFolder%\*.mat") do (
set "matfile=%%~nf"
matc -p mobile -a opengl -o "!matfile!.filamat" "%%f"
move "!matfile!.filamat" "%distFolder%\!matfile!.filamat"
) echo Processing complete.
pause

​ 说明:需要将 matc.exe 文件与 transform.bat 文件放在同一个目录下面,matc.exe 源自Filament环境搭建中编译生成的 exe 文件。双击 transform.bat 文件,会自动将 /src/main/materials/ 下面的所有 mat 文件全部转换为 filamat 文件,并移到 /src/main/assets/materials/ 目录下面。

​ 运行效果如下。

​ 声明:本文转自【Filament】纹理贴图

【Filament】纹理贴图的更多相关文章

  1. [Unity] Shader(着色器)之纹理贴图

    在Shader中,我们除了可以设定各种光线处理外,还可以增加纹理贴图. 使用 settexture 命令可以为着色器指定纹理. 示例代码: Shader "Sbin/ff2" { ...

  2. IOS 中openGL使用教程3(openGL ES 入门篇 | 纹理贴图(texture)使用)

    在这篇文章中,我们将学习如何在openGL中使用纹理贴图. penGL中纹理可以分为1D,2D和3D纹理,我们在绑定纹理对象的时候需要指定纹理的种类.由于本文将以一张图片为例,因此我们为我们的纹理对象 ...

  3. Unity3D ShaderLab压缩混合纹理贴图

    Unity3D ShaderLab压缩混合纹理贴图 纹理可以用于存储大量的数据,我们可以把多个图像打包存储在单一的RGBA纹理上,然后通过着色器代码提取这些元素, 我们就可以使用每个图片的RGBA通道 ...

  4. OpenGL 纹理贴图

    前一节实例代码中有个贴图操作. 今天就简单说明一下纹理贴图... 为了使用纹理贴图.我们首先需要启用纹理贴图功能. 我们可以在Renderer实现的onSurfaceCreated中定义启用: // ...

  5. (转载)Cocos2dx-OpenGL ES2.0教程:纹理贴图(6)

    在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-P ...

  6. Directx11学习笔记【十七】纹理贴图

    本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5596180.html 在之前的例子中,我们实现了光照和材质使得场景 ...

  7. android ndk调用OpenGL 实现纹理贴图Texture

    android ndk调用OpenGL 实现纹理贴图Texture 时间 2014-06-25 05:24:39  CSDN博客 原文  http://blog.csdn.net/chrisfxs/a ...

  8. Android OpenGL ES 开发(九): OpenGL ES 纹理贴图

    一.概念 一般说来,纹理是表示物体表面的一幅或几幅二维图形,也称纹理贴图(texture).当把纹理按照特定的方式映射到物体表面上的时候,能使物体看上去更加真实.当前流行的图形系统中,纹理绘制已经成为 ...

  9. WebGL学习之纹理贴图

    为了使图形能获得接近于真实物体的材质效果,一般会使用贴图,贴图类型主要包括两种:漫反射贴图和镜面高光贴图.其中漫反射贴图可以同时实现漫反射光和环境光的效果. 实际效果请看demo:纹理贴图 2D纹理 ...

  10. WebGL three.js学习笔记 纹理贴图模拟太阳系运转

    纹理贴图的应用以及实现一个太阳系的自转公转 点击查看demo演示 demo地址:https://nsytsqdtn.github.io/demo/solar/solar three.js中的纹理 纹理 ...

随机推荐

  1. Oracle数据库无法启动的简单处理

    1. 最近一台测试机器上面的Oracle数据库启动不起来了. 提示信息是UNDOTBS2的表空间找不到. 2. 然后可以使用 startup mount 简单开起来 但是发现还是无法使用. 3.本来想 ...

  2. 什么是PWA 应用?核心技术有哪些

    在国内由于小程序的风生水起,PWA 应用在国内的状况一直都不是很好,PWA 和小程序有很多的相似性,但是 PWA 是由谷歌发起的技术,小程序是微信发起的技术,所以小程序在国内得到了大力的扶持,很快就在 ...

  3. 大数据平台Bug Bash大扫除最佳实践

    一.背景 随着越来越多的"新人"在日常工作以及大促备战中担当大任,我们发现仅了解自身系统业务已不能满足日常系统开发运维需求.为此,大数据平台部门组织了一次Bug Bash活动,既能 ...

  4. el-popover 点击取消按钮,弹窗仍然无法关闭

    <el-popover placement="bottom" width="200" :ref="aa" :visible.sync= ...

  5. forEach在项目中的使用

    forEach 会改变原始数组 被forEach循环的数组不能够为空 forEach会改变原始数组 value是内容 index是索引 array是你写的数组. foeEach内部是异步的哈 功能描述 ...

  6. Spring源码之XML文件中Bean标签的解析1

    读取XML文件,创建对象 xml文件里包含Bean的信息,为了避免多次IO,需要一次性读取xml文件中所有bean信息,加入到Spring工厂. 读取配置文件 new ClassPathResourc ...

  7. 英伟达系列显卡大解析B100、H200、L40S、A100、A800、H100、H800、V100如何选择,含架构技术和性能对比带你解决疑惑

    英伟达系列显卡大解析B100.H200.L40S.A100.A800.H100.H800.V100如何选择,含架构技术和性能对比带你解决疑惑 近期,AIGC领域呈现出一片繁荣景象,其背后离不开强大算力 ...

  8. 4.2 Windows驱动开发:内核中进程线程与模块

    内核进程线程和模块是操作系统内核中非常重要的概念.它们是操作系统的核心部分,用于管理系统资源和处理系统请求.在驱动安全开发中,理解内核进程线程和模块的概念对于编写安全的内核驱动程序至关重要. 内核进程 ...

  9. 14.4 Socket 双向数据通信

    所谓双向数据传输指的是客户端与服务端之间可以无差异的实现数据交互,此类功能实现的核心原理是通过创建CreateThread()函数多线程分别接收和发送数据包,这样一旦套接字被建立则两者都可以异步发送消 ...

  10. LyScriptTools 模块类API接口手册

    LyScriptTools工具包是在LyScript模块基础上封装的工具包,其主要是二次封装LyScript插件实现的一些新功能,或者将特定功能组件拆分开形成的独立模块,此类模块可实现更加精细化的功能 ...