第一个程序-绘制三角形(一)

本文介绍了如何在Android应用中使用Filament渲染引擎绘制三角形。首先,通过Android Studio添加Filament依赖,接着讲解了初始化引擎环境、创建Surface的重要性。分析了SurfaceView和TextureView在构建Surface中的角色,以及Filament如何通过SwapChain与Surface关联。最后提到了在Filament中渲染前必须创建的关键对象,为后续的渲染配置做准备。
部署运行你感兴趣的模型镜像

    上篇在文章 Filament简介 中对Filament总体做了简单的介绍,了解到Filament是一个小巧但很

高效的渲染引擎。像其它程序学习初期的HelloWorld一样,本章我们开始介绍如何使用Filament

绘制一个简单的三角形。

    在开始绘制三角形之前,Filament中有几个重要概念我们做一个简单的说明:

Engine可以理解为Filament的程序入口点,主要用来跟踪用户创建的所有资源,管理渲染线程和硬件渲染。
Renderer可以理解为操作系统的窗口
SwapChain可以理解为操作系统的可以绘制的画布
View        用来保存可以绘制对象及其相关信息,一个View跟一个Scene相关联
Scene可以理解为容器,包含光照和可绘制实体信息
Camera相机信息包含投影矩阵信息及其曝光值参数

    在开始绘制之前,我们首先要做的就是准备环境,在 Android 平台我们开发APP,主要使用

Android Studio 作为IDE,我们要做的就是将需要的依赖库文件添加到编译文件中:

dependencies {
    implementation 'com.google.android.filament:filament-android:1.27.1'
    implementation 'com.google.android.filament:filament-utils-android:1.27.1'
    implementation 'com.google.android.filament:gltfio-android:1.27.1'
    implementation 'com.google.android.filament:filamat-android:1.27.1'
}

    上面引用的依赖有版本信息,Filament目前在不断更新,目前(2023-01-31)最新的版本为

v1.31.2。添加了依赖之后,我们就可以在 Android Studio 工程中进行开发。在 Android 平台上

使用 Filament 首先要做的事情就是调用:

Filament.init()

    其作用就是初始化引擎环境,主要是加载so库,我们来看一下这里面的具体实现:

public class Filament {
    static {
        Platform.get();
        System.loadLibrary("filament-jni");
    }

    private Filament() {
    }

    @SuppressWarnings("unused")
    public static void init() {
    }
}

   上面代码主要做了两件事情:

    1、初始化平台 Platform.get, 在 Android 平台主要是构建 AndroidPlatform 对象

    2、加载库文件 filament-jni.so

    以上工作准备完毕之后,就可以着手绘制工作。

    绘制工作首先要做的工作就是准备画布。如果使用OpenGL绘制图形,在不同的平

台上需要不同的画布,在Windows平台上需要Window,在Android平台则需要 Surface。那么在

Android平台上如何提供这个Surface画布呢?

    Android 平台 提供了两个控件可以提供Surface画布:

1、SurfaceView

2、TextureView

我们首先分析一下,这两个控件是如何构建 Android 平台 Surface 的。

1、通过 SurfaceView 构建 Surface

      我们看一下 Android 平台 源码中 SurfaceView的主要相关代码:

public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCallback {

   private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
        private static final String LOG_TAG = "SurfaceHolder";


        @Override
        public Surface getSurface() {
            return mSurface;
        }

    };

    /**
     * Return the SurfaceHolder providing access and control over this
     * SurfaceView's underlying surface.
     *
     * @return SurfaceHolder The holder of the surface.
     */
    public SurfaceHolder getHolder() {
        return mSurfaceHolder;
    }
}

    上述我们只列出了与Surface相关的主要代码,可以看到,可以先获取到 SurfaceHolder,然后

通过 SurfaceHolder 的成员方法 getSurface 获取到 Surface对象。

2、通过 TextureView 构建 Surface

      同样的,我们看下 TextureView 的源码:

public class TextureView extends View {

    /**
     * Returns the {@link SurfaceTexture} used by this view. This method
     * may return null if the view is not attached to a window or if the surface
     * texture has not been initialized yet.
     *
     * @see #isAvailable()
     */
    public @Nullable SurfaceTexture getSurfaceTexture() {
        return mSurface;
    }
}

   通过 TextureView 的 getSurfaceTexture() 方法获取 SurfaceTexture对象,而后可以通过

SurfaceTexture 构建 Surface 对象:

public class Surface implements Parcelable {

    public Surface(SurfaceTexture surfaceTexture) {
        if (surfaceTexture == null) {
            throw new IllegalArgumentException("surfaceTexture must not be null");
        }
        mIsSingleBuffered = surfaceTexture.isSingleBuffered();
        synchronized (mLock) {
            mName = surfaceTexture.toString();
            setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
        }
    }

}

    如上,在 Android 源码中, Surface 提供了以 SurfaceTexture 为参数的构造方法。

    现在我们知道如何利用 Android 平台的基础控件获取到画布,但 Filament 渲染引擎提供的

画布为 SwapChain ,那么 Filament 是如何将二者联系起来的?


    /**
     * Creates an opaque {@link SwapChain} from the given OS native window handle.
     *
     * @param surface on Android, <b>must be</b> an instance of {@link android.view.Surface}
     *
     * @return a newly created {@link SwapChain} object
     *
     * @exception IllegalStateException can be thrown if the SwapChain couldn't be created
     */
    @NonNull
    public SwapChain createSwapChain(@NonNull Object surface) {
        return createSwapChain(surface, SwapChain.CONFIG_DEFAULT);
    }

    Filament 的 Engine 类提供了成员方法 createSwapChain 将 Surface 和 SwapChain 联系了起

来。在 Filament 中 通过 UiHelper 将 Android 中控件与 SwapChain 联系起来。

public class UiHelper {
    /**
     * Interface used to know when the native surface is created, destroyed or resized.
     *
     * @see #setRenderCallback(RendererCallback)
     */
    public interface RendererCallback {
        /**
         * Called when the underlying native window has changed.
         */
        void onNativeWindowChanged(Surface surface);

        /**
         * Called when the surface is going away. After this call <code>isReadyToRender()</code>
         * returns false. You MUST have stopped drawing when returning.
         * This is called from detach() or if the surface disappears on its own.
         */
        void onDetachedFromSurface();

        /**
         * Called when the underlying native window has been resized.
         */
        void onResized(int width, int height);
    }

}

 UiHelper 中提供了一个接口 RenderCallback,需要用户实现该接口,主要用于创建SwapChain

    class SurfaceCallback implements UiHelper.RendererCallback {

        @Override
        public void onNativeWindowChanged(Surface surface) {
            mSwapChain = mEngine.createSwapChain(surface);
        }

        @Override
        public void onDetachedFromSurface() {
            mEngine.destroySwapChain(mSwapChain);
        }

        @Override
        public void onResized(int width, int height) {

        }
    }

     实现了该接口之后,将 SurfaceView 和 UiHelper 联系起来:

UiHelper uiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
uiHelper.setRenderCallback(new SurfaceCallback());
uiHelper.attachTo(mSurfaceView);

    到这里我们梳理一下,我们通过 Android 的控件 SurfaceView 与 Filament 的 UiHelper 联系起

来,用于提供画布。也就是说,我们目前将画布准备好了。

    下面我们就要学习该如何使用 Filament 进行渲染三角形。开始渲染之前,Filament 要求我们

必须创建几个我们在本章开篇提到的几个对象:

Engine engine = Engine.create();
Renderer renderer = engine.createRenderer();
Scene scene = engine.createScene();
View view = engine.createView();
Camera camera = engine.createCamera(engine.getEntityManager().create());

    Filament 的渲染工作离不开上面几个对象,后面就需要对各个对象进行配置,最后调用下面

的语句进行具体的渲染:

        if (renderer.beginFrame(mSwapChain, frameTimeNanos)) {
            renderer.render(view);
            renderer.endFrame();
        }

    其中mSwapChain 是我们前面利用 Surface 创建的对象, frameTimeNanos 是 Android 平台

的时间戳, 可通过 System.nanoTime() 获取当前时间戳。view 对象是我们前面创建的,当然

现在我们还没有对view进行配置,即使调用了以上代码也无发渲染出任何内容,我们在

下一章分析讲解具体如何配置这几个对象。

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think>好的,用户想用Python设计程序绘制三角形。首先,我需要确定用户想要的三角形类型是哪种,是等边三角形、直角三角形,还是任意三角形。不过通常来说,如果是编程练习,最常见的可能是绘制个等边三角形或者直角三角形。 接下来,用户可能希望图形是字符绘制的,比如用星号(*)在控制台输出,或者使用图形库比如turtle来绘制可视化的图形。需要进步分析用户的需求。如果是初学者,可能更倾向于简单的字符打印,而如果是图形界面,可能需要用到turtle库或者matplotlib等库。 假设用户是刚开始学习Python,可能更希望看到两种方法:种是控制台打印三角形,另种是用turtle绘图。这样用户可以根据自己的需要选择。 首先考虑字符打印的方式。例如,打印个等边三角形,每行逐渐增加星号的数量。例如,第行1个星号,第二行3个,第三行5个,以此类推,但可能需要调整间距来形成三角形形状。或者更简单的,比如三行星号,每行增加前面的空格数,使星号居中。例如: * *** ***** 但这样可能需要确定层数,并计算每行的空格和星号数量。或者可能是直角三角形,每行递增星号数量,例如: * ** *** 这种情况下,可以用循环控制行数和每行的星号数。 另种方法是使用turtle库,通过控制海龟移动来绘制三角形。例如,画个等边三角形,每条边长度相同,转向120度。或者画任意三角形,输入三个边的长度或角度,但可能比较复杂。对于用户来说,可能更倾向于简单示例,比如用turtle画等边三角形。 现在需要判断用户的需求。如果用户没有明确说明,可能需要提供两种方法的示例,并解释其原理。同时,要确保代码正确,语法无误。 对于字符打印的直角三角形,代码可能如下: rows = 3 for i in range(1, rows+1): print('*' * i) 这样会输出: * ** *** 但如果是等边三角形,需要处理空格和星号的位置。例如: n = 3 for i in range(n): print(' ' * (n - i - 1) + '*' * (2*i + 1)) 输出: * *** ***** 但可能行数需要调整,或者用户希望的是空心三角形,这时候需要处理只在边缘打印星号,中间留空,不过这可能比较复杂,用户可能只是需要实心三角形。 如果是使用turtle库,代码可能如下: import turtle turtle.forward(100) # 画第条边 turtle.left(120) turtle.forward(100) # 第二条边 turtle.left(120) turtle.forward(100) # 第三条边 turtle.done() 这样会画出个等边三角形。但需要确保最后转向正确,或者是否闭合。例如,转向角度是否正确,或者是否有重复的边。 另外,用户可能需要输入三角形的三个顶点坐标,然后绘制,但这种情况可能需要更复杂的计算或者使用图形库如matplotlib。不过对于入门者来说,turtle可能更简单。 现在需要组织回答,分步骤说明两种方法,并给出示例代码。同时要确保代码正确,比如turtle的方向是否正确,循环是否正确处理空格和星号的数量。 检查可能的错误点:比如在字符三角形中,循环的范围是否正确,空格的计算是否正确。例如,对于n=3层,每层的空格数是n - i -1,星号数是2i+1。当i从0到2时,第层i=0,空格是2,星号1;第二层i=1,空格1,星号3;第三层i=2,空格0,星号5。这样可能层数对应的行数是否正确,或者用户是否希望行数等于输入的n。例如,用户输入3,输出3行,这样是正确的。 总结:提供两种方法,字符打印和turtle绘图,分别给出示例代码,并解释每步的作用。确保代码正确,并符合用户的不同需求层次。</think>让我们分步骤实现两种常见的三角形绘制方法: ### 方法:控制台字符三角形(等边) ```python n = 5 # 定义三角形层数 for i in range(n): # 打印空格和星号组合 print(' ' * (n - i - 1) + '*' * (2*i + 1)) ``` 输出效果: ``` * *** ***** ******* ********* ``` **代码解析**: 1. `n` 控制三角形的高度(层数) 2. 外层循环控制行数 3. 每行由空格和星号组成: - 空格数 = 总层数 - 当前行数 - 1 - 星号数 = 2 × 当前行数 + 1 ### 方法二:Turtle图形库绘制(等边三角形) ```python import turtle pen = turtle.Turtle() side_length = 100 # 设置边长 for _ in range(3): pen.forward(side_length) pen.left(120) # 等边三角形内角为60度,转向补角120度 turtle.done() # 保持窗口显示 ``` **代码解析**: 1. 导入海龟绘图库 2. 创建画笔对象 3. 循环3次绘制三条边: - 每次前进指定长度 - 左转120度形成60度内角 4. 保持绘图窗口 ### 扩展方案:直角三角形(带用户输入) ```python n = int(input("请输入三角形高度: ")) for row in range(1, n+1): print('*' * row) ``` 示例输入`5`时输出: ``` * ** *** **** ***** ``` ### 关键知识点 1. 循环结构:`for`循环控制图形层数 2. 字符串操作:乘法运算符生成重复字符 3. 几何计算:等边三角形的角度计算(内角60°,转向补角120°) 4. 坐标系统:Turtle库使用笛卡尔坐标系 可根据具体需求选择实现方式: - 控制台打印适合快速验证逻辑 - Turtle库适合可视化教学 - 输入验证和异常处理可提升程序健壮性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后知后觉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值