Практика формирования видеоряда напрямую из 3D программ, созданных на Unity/Unreal/OpenSceneGraph для создания 3D-обучающих видеофильмов показала высокую эффективность такого подхода. Качество синтезируемой модели часто практически не уступает по фотореалистичности видео, созданному “классически”, т.е. традиционным рендерингом из программ 3dMax/Maya/Cinema и т.д.

Пример видеоряда из Unity 3D / Трубопроводный транспорт. КПП СОД.
Пример видеоряда из Unity 3D / Трубопроводный транспорт. КПП СОД.
Пример видеоряда из Unity
Пример видеоряда из Unity
Еще пример
Еще пример
Еще пример
Еще пример

При этом имеется возможность управления камерой в режиме реального времени, используя стандартные средства ввода-вывода / клавиатура-мышь. Для выполнения простых пролетов камеры, например по орбите с равномерным приближением или отдалением такой подход является достаточным и не требует каких либо дополнительных устройств управления. Однако при сложных движениях камеры требуется контролировать большее количество параметров движения и как следствие использование стандартных средств ввода-вывода становится или неудобным или недостаточным. VR также не всегда применим, особенно при больших размерах объекта и/или при быстрых перемещениях камеры.

На основе нашего опыта создания видеоряда из 3D программ было принято решение создать пульт управления виртуальной камерой, для эффективного управления движениями камеры, управлением масштабов времени и реализацией управления сценарием происходящего в 3D.

В качестве базы было выбрано оборудование, имеющееся “в запасе”, а именно:

Arduino Due — плата микроконтроллера на базе процессора Atmel SAM3X8E ARM Cortex-M3 (описание). Это первая плата Arduino на основе 32-битного микроконтроллера с ARM ядром. На ней имеется 54 цифровых вход/выхода (из них 12 можно задействовать под выходы ШИМ), 12 аналоговых входов, 4 UARTа (аппаратных последовательных порта), a генератор тактовой частоты 84 МГц, связь по USB с поддержкой OTG, 2 ЦАП (цифро-аналоговых преобразователя), 2 TWI, разъем питания,  разъем SPI, разъем JTAG. Частота процессора (CPU) 84 МГц. 96 КБ ОЗУ. 512 КБ флеш-памяти для хранения программ. контроллер DMA, который разгружает центральный процессор от выполнения интенсивных операций с памятью.

Да, немного перебор, но она была под рукой.

TFT-дисплей на базе ILI9341 с тачскрином (320*240 65к цветов)

Энкодер с кнопкой, переменные резисторы, два модуля джойстиков, переключатели и кнопки с фиксацией и без фксации.

Фото устройства в процессе монтажа в корпусе.

Код написан в Arduino IDE, с использованием библиотек:

  • include “SPI.h”

  • include “Adafruit_GFX.h”

  • include “Adafruit_ILI9341.h”

  • include “Joystick.h”

Код программы приведен в конце статьи, а пока результат:

Внутренности после монтажа (клавиши еще не смонтированы)

Вот так операционная система определяет устройство (можно поменять конечно)

Ну и собственно тестирование.

Исходный код программы:
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Joystick.h"
#include <EncButton2.h>



Joystick_ Joystick;

/*
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_MULTI_AXIS, 32, 0,
  true, true, false, false, false, false,
  true, true, false, false, false);
*/

const int analogInPin1 = A0;
const int analogInPin2 = A1;
const int analogInPin3 = A2;
const int analogInPin4 = A3;

const int analogInPin5 = A4;
const int analogInPin6 = A5;
const int analogInPin7 = A6;



// For the Adafruit shield, these are the default.
#define TFT_RST 8
#define TFT_DC 9
#define TFT_CS 10
#define TFT_MISO 50
#define TFT_MOSI 51
#define TFT_SCK 52
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
// If using the breakout, change pins as desired

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST, TFT_MISO);

EncButton2<EB_ENC> enc(INPUT, 45, 43);        // просто энкодер



// the setup function runs once when you press reset or power the board
void setup() {

  //Serial.begin(9600);

  // Set Range Values
  Joystick.setXAxisRange(-127, 127);
  Joystick.setYAxisRange(-127, 127);
  Joystick.setZAxisRange(0, 255);
  Joystick.setRxAxisRange(255, 0);
  Joystick.setRyAxisRange(0, 255);
  Joystick.setRzAxisRange(255, 0);
  Joystick.setThrottleRange(0, 255);
  Joystick.setRudderRange(0, 255);

  Joystick.setAcceleratorRange(0, 255);
  Joystick.setBrakeRange(0, 255);
  Joystick.setSteeringRange(0, 255);

 
  
 
  Joystick.begin(false);
 
  
  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  pinMode(A3, INPUT_PULLUP);
  pinMode(A4, INPUT_PULLUP);
  pinMode(A5, INPUT_PULLUP);
  pinMode(A6, INPUT_PULLUP);
  pinMode(A7, INPUT_PULLUP);

  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);

  //pinMode(13, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  pinMode(47, INPUT); //butt0 encoder
  pinMode(45, INPUT); //encoder1
  pinMode(43, INPUT); //encoder2

  pinMode(38, INPUT); //butt1
  pinMode(40, INPUT); //butt2

   tft.begin();

  // read diagnostics (optional but can help debug problems)
  uint8_t x = tft.readcommand8(ILI9341_RDMODE);
  //Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDMADCTL);
  //Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDPIXFMT);
 // Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDIMGFMT);
 // Serial.print("Image Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDSELFDIAG);
 // Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); 

  tft.setRotation(0); //0 1 2 3 

  tft.fillScreen(ILI9341_BLACK);
  //unsigned long start = micros();
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_WHITE); 
  tft.setTextSize(2);
  tft.println("Lcontent.ru");
  tft.setTextColor(ILI9341_BLUE); 
  tft.println("Joy module v.1.0");
  tft.setTextColor(ILI9341_RED); 
  tft.println("by Maxim Gammer");
  
}

int OLD_sensorValue1 = 0; 
int encoderValue=0;
// the loop function runs over and over again forever
void loop() 
{
    int sensorValue1 = 0; 
    int outputValue1 = 0;  
    int sensorValue2 = 0; 
    int outputValue2 = 0; 
    int sensorValue3 = 0; 
    int outputValue3 = 0; 
    int sensorValue4 = 0; 
    int outputValue4 = 0; 

    int sensorValue5 = 0; 
    int outputValue5 = 0; 
    int sensorValue6 = 0; 
    int outputValue6 = 0; 
    int sensorValue7 = 0; 
    int outputValue7 = 0; 

    sensorValue1 = analogRead(analogInPin1);
    sensorValue2 = analogRead(analogInPin2);
    sensorValue3 = analogRead(analogInPin3);
    sensorValue4 = analogRead(analogInPin4);

    sensorValue5 = analogRead(analogInPin5);
    sensorValue6 = analogRead(analogInPin6);
    sensorValue7 = analogRead(analogInPin7);
    
    // map it to the range of the analog out:
    outputValue1 = map(sensorValue1, 0, 1023, 0, 255);
    outputValue2 = map(sensorValue2, 0, 1023, 0, 255);
    outputValue3 = map(sensorValue3, 0, 1023, 0, 255);
    outputValue4 = map(sensorValue4, 0, 1023, 0, 255);
    outputValue5 = map(sensorValue5, 0, 1023, 0, 255);
    outputValue6 = map(sensorValue6, 0, 1023, 0, 255);
    outputValue7 = map(sensorValue7, 0, 1023, 0, 255);
    
    Joystick.setYAxis(outputValue1 - 128);
    Joystick.setXAxis(outputValue2 - 128);
    Joystick.setRxAxis(outputValue3);
    Joystick.setRyAxis(outputValue4);
    
    
    
    Joystick.setZAxis(outputValue5);
    Joystick.setRzAxis(outputValue6);
    Joystick.setThrottle(outputValue7);
    
    //Joystick.setRudder(255);
    Joystick.setRudder(255);
    Joystick.setAccelerator(255);
    Joystick.setBrake(255);
    Joystick.setSteering(255);
    
    int buttonState0 = digitalRead(47);
    Joystick.setButton(0, buttonState0);

    int buttonState1 = digitalRead(38);
    Joystick.setButton(1, buttonState1);

    int buttonState2 = digitalRead(40);
    Joystick.setButton(2, buttonState2);

    

    enc.tick();                       // опрос происходит здесь
    if (enc.left()) 
    {
      encoderValue = encoderValue+45;      
    }
    if (enc.right()) 
    {
      encoderValue = encoderValue-45;
    }
    if (encoderValue<=-45) 
    {
      encoderValue=315;
    }
    else if (encoderValue>=360)
    {
      encoderValue=0;
    }
    int hatSwitch =0;
    Joystick.setHatSwitch(hatSwitch, -1);
    Joystick.setHatSwitch(hatSwitch, encoderValue); //0 45  90  135 180  225 270 315
    

    Joystick.sendState();

    
    //Serial.print("sensor = ");
    //Serial.println(sensorValue);

   /*
   char TX[20];
   //tft.fillRect(0, 0, 40, 10, ILI9341_BLACK); // textbgcolor is protected in Adafruit_GFX.h 
   tft.setTextSize(1);
   tft.setCursor(0, 0);
   tft.setTextColor(ILI9341_BLACK); 
   sprintf(TX,"%4d", OLD_sensorValue1);
   tft.println(TX);
   OLD_sensorValue1 = sensorValue1;
   tft.setCursor(0, 0);
   tft.setTextColor(ILI9341_WHITE); 
   sprintf(TX,"%4d", sensorValue1);
   tft.println(TX); //"Hello World!"
    
    delay(2);
   */
  
  //digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  //delay(1000);                       // wait for a second
  //digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  //delay(1000);                       // wait for a second
}

Комментарии (5)


  1. defecator
    16.03.2022 22:38
    +2

    а можно узнать, что там за джойстики ?

    Желательно со ссылочкой на али - давно ищу что-то подобное


    1. maxgammer Автор
      17.03.2022 07:28


  1. Mc_Key
    17.03.2022 07:28
    +1

    Проще было купить аппаратуру RC, типа FlySky i6, ещё и дешевле выйдет учитывая стоимость этой Arduino.


    1. maxgammer Автор
      17.03.2022 07:29

      Ну не совсем) Мне например нужно текст выводить на экран по сценарию фильма)) + энкодеры нужны все-таки